void ThrowSaberToAttacker(gentity_t *self, gentity_t *attacker) { gentity_t *ent = &g_entities[self->client->ps.saberIndex]; vec3_t a; int altVelocity = 0; if (!ent || ent->enemy != self) { //something has gone very wrong (this should never happen) //but in case it does.. find the saber manually #ifdef _DEBUG Com_Printf("Lost the saber! Attempting to use global pointer..\n"); #endif ent = gJMSaberEnt; if (!ent) { #ifdef _DEBUG Com_Printf("The global pointer was NULL. This is a bad thing.\n"); #endif return; } #ifdef _DEBUG Com_Printf("Got it (%i). Setting enemy to client %i.\n", ent->s.number, self->s.number); #endif ent->enemy = self; self->client->ps.saberIndex = ent->s.number; } trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, "-1" ); if (attacker && attacker->client && self->client->ps.saberInFlight) { //someone killed us and we had the saber thrown, so actually move this saber to the saber location //if we killed ourselves with saber thrown, however, same suicide rules of respawning at spawn spot still //apply. gentity_t *flyingsaber = &g_entities[self->client->ps.saberEntityNum]; if (flyingsaber && flyingsaber->inuse) { VectorCopy(flyingsaber->s.pos.trBase, ent->s.pos.trBase); VectorCopy(flyingsaber->s.pos.trDelta, ent->s.pos.trDelta); VectorCopy(flyingsaber->s.apos.trBase, ent->s.apos.trBase); VectorCopy(flyingsaber->s.apos.trDelta, ent->s.apos.trDelta); VectorCopy(flyingsaber->r.currentOrigin, ent->r.currentOrigin); VectorCopy(flyingsaber->r.currentAngles, ent->r.currentAngles); altVelocity = 1; } } self->client->ps.saberInFlight = qtrue; //say he threw it anyway in order to properly remove from dead body ent->s.modelindex = G_ModelIndex("models/weapons2/saber/saber_w.glm"); ent->s.eFlags &= ~(EF_NODRAW); ent->s.modelGhoul2 = 1; ent->s.eType = ET_MISSILE; ent->enemy = NULL; if (!attacker || !attacker->client) { VectorCopy(ent->s.origin2, ent->s.pos.trBase); VectorCopy(ent->s.origin2, ent->s.origin); VectorCopy(ent->s.origin2, ent->r.currentOrigin); ent->pos2[0] = 0; trap_LinkEntity(ent); return; } if (!altVelocity) { VectorCopy(self->s.pos.trBase, ent->s.pos.trBase); VectorCopy(self->s.pos.trBase, ent->s.origin); VectorCopy(self->s.pos.trBase, ent->r.currentOrigin); VectorSubtract(attacker->client->ps.origin, ent->s.pos.trBase, a); VectorNormalize(a); ent->s.pos.trDelta[0] = a[0]*256; ent->s.pos.trDelta[1] = a[1]*256; ent->s.pos.trDelta[2] = 256; } trap_LinkEntity(ent); }
void SP_worldspawn( void ) { char *text, temp[32]; int i; int lengthRed, lengthBlue, lengthGreen; //I want to "cull" entities out of net sends to clients to reduce //net traffic on our larger open maps -rww G_SpawnFloat("distanceCull", "6000.0", &g_cullDistance); trap_SetServerCull(g_cullDistance); G_SpawnString( "classname", "", &text ); if ( Q_stricmp( text, "worldspawn" ) ) { G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" ); } for ( i = 0 ; i < level.numSpawnVars ; i++ ) { if ( Q_stricmp( "spawnscript", level.spawnVars[i][0] ) == 0 ) {//ONly let them set spawnscript, we don't want them setting an angle or something on the world. BG_ParseField( fields, level.spawnVars[i][0], level.spawnVars[i][1], (byte *)&g_entities[ENTITYNUM_WORLD] ); } } //The server will precache the standard model and animations, so that there is no hit //when the first client connnects. if (!BGPAFtextLoaded) { BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", bgHumanoidAnimations, qtrue); } if (!precachedKyle) { int defSkin; trap_G2API_InitGhoul2Model(&precachedKyle, "models/players/kyle/model.glm", 0, 0, -20, 0, 0); if (precachedKyle) { defSkin = trap_R_RegisterSkin("models/players/kyle/model_default.skin"); trap_G2API_SetSkin(precachedKyle, 0, defSkin, defSkin); } } if (!g2SaberInstance) { trap_G2API_InitGhoul2Model(&g2SaberInstance, "models/weapons2/saber/saber_w.glm", 0, 0, -20, 0, 0); if (g2SaberInstance) { // indicate we will be bolted to model 0 (ie the player) on bolt 0 (always the right hand) when we get copied trap_G2API_SetBoltInfo(g2SaberInstance, 0, 0); // now set up the gun bolt on it trap_G2API_AddBolt(g2SaberInstance, 0, "*blade1"); } } if (g_gametype.integer == GT_SIEGE) { //a tad bit of a hack, but.. EWebPrecache(); } // make some data visible to connecting client trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION ); trap_SetConfigstring( CS_LEVEL_START_TIME, va("%i", level.startTime ) ); G_SpawnString( "music", "", &text ); trap_SetConfigstring( CS_MUSIC, text ); G_SpawnString( "message", "", &text ); trap_SetConfigstring( CS_MESSAGE, text ); // map specific message trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day G_SpawnString( "gravity", "800", &text ); trap_Cvar_Set( "g_gravity", text ); G_SpawnString( "enableBreath", "0", &text ); trap_Cvar_Set( "g_enableBreath", text ); G_SpawnString( "soundSet", "default", &text ); trap_SetConfigstring( CS_GLOBAL_AMBIENT_SET, text ); g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD; g_entities[ENTITYNUM_WORLD].classname = "worldspawn"; // see if we want a warmup time trap_SetConfigstring( CS_WARMUP, "" ); if ( g_restarted.integer ) { trap_Cvar_Set( "g_restarted", "0" ); level.warmupTime = 0; } /* else if ( g_doWarmup.integer && g_gametype.integer != GT_DUEL && g_gametype.integer != GT_POWERDUEL ) { // Turn it on level.warmupTime = -1; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); G_LogPrintf( "Warmup:\n" ); } */ trap_SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+0, defaultStyles[0][0]); trap_SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+1, defaultStyles[0][1]); trap_SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+2, defaultStyles[0][2]); for(i=1;i<LS_NUM_STYLES;i++) { Com_sprintf(temp, sizeof(temp), "ls_%dr", i); G_SpawnString(temp, defaultStyles[i][0], &text); lengthRed = strlen(text); trap_SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+0, text); Com_sprintf(temp, sizeof(temp), "ls_%dg", i); G_SpawnString(temp, defaultStyles[i][1], &text); lengthGreen = strlen(text); trap_SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+1, text); Com_sprintf(temp, sizeof(temp), "ls_%db", i); G_SpawnString(temp, defaultStyles[i][2], &text); lengthBlue = strlen(text); trap_SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+2, text); if (lengthRed != lengthGreen || lengthGreen != lengthBlue) { Com_Error(ERR_DROP, "Style %d has inconsistent lengths: R %d, G %d, B %d", i, lengthRed, lengthGreen, lengthBlue); } } }
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 ); }
/*QUAKED worldspawn (0 0 0) ? Every map should have exactly one worldspawn. "music" music wav file "gravity" 800 is default gravity "message" Text to print during connection process */ void SP_worldspawn( void ) { char *s; G_SpawnString( "classname", "", &s ); if( Q_stricmp( s, "worldspawn" ) ) G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" ); // make some data visible to connecting client trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION ); trap_SetConfigstring( CS_LEVEL_START_TIME, va( "%i", level.startTime ) ); G_SpawnString( "music", "", &s ); trap_SetConfigstring( CS_MUSIC, s ); G_SpawnString( "message", "", &s ); trap_SetConfigstring( CS_MESSAGE, s ); // map specific message trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day G_SpawnString( "gravity", "800", &s ); trap_Cvar_Set( "g_gravity", s ); G_SpawnString( "humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, &s ); trap_Cvar_Set( "g_humanBuildPoints", s ); G_SpawnString( "humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, &s ); trap_Cvar_Set( "g_humanMaxStage", s ); G_SpawnString( "humanStage2Threshold", DEFAULT_HUMAN_STAGE2_THRESH, &s ); trap_Cvar_Set( "g_humanStage2Threshold", s ); G_SpawnString( "humanStage3Threshold", DEFAULT_HUMAN_STAGE3_THRESH, &s ); trap_Cvar_Set( "g_humanStage3Threshold", s ); G_SpawnString( "alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, &s ); trap_Cvar_Set( "g_alienBuildPoints", s ); G_SpawnString( "alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, &s ); trap_Cvar_Set( "g_alienMaxStage", s ); G_SpawnString( "alienStage2Threshold", DEFAULT_ALIEN_STAGE2_THRESH, &s ); trap_Cvar_Set( "g_alienStage2Threshold", s ); G_SpawnString( "alienStage3Threshold", DEFAULT_ALIEN_STAGE3_THRESH, &s ); trap_Cvar_Set( "g_alienStage3Threshold", s ); G_SpawnString( "enableDust", "0", &s ); trap_Cvar_Set( "g_enableDust", s ); G_SpawnString( "enableBreath", "0", &s ); trap_Cvar_Set( "g_enableBreath", s ); G_SpawnString( "disabledEquipment", "", &s ); trap_Cvar_Set( "g_disabledEquipment", s ); G_SpawnString( "disabledClasses", "", &s ); trap_Cvar_Set( "g_disabledClasses", s ); G_SpawnString( "disabledBuildables", "", &s ); trap_Cvar_Set( "g_disabledBuildables", s ); g_entities[ ENTITYNUM_WORLD ].s.number = ENTITYNUM_WORLD; g_entities[ ENTITYNUM_WORLD ].classname = "worldspawn"; // see if we want a warmup time trap_SetConfigstring( CS_WARMUP, "" ); if( g_restarted.integer ) { trap_Cvar_Set( "g_restarted", "0" ); level.warmupTime = 0; } else if( g_doWarmup.integer ) { // Turn it on level.warmupTime = -1; trap_SetConfigstring( CS_WARMUP, va( "%i", level.warmupTime ) ); G_LogPrintf( "Warmup:\n" ); } }
void SP_trigger_objective_info( gentity_t *ent ) { char *scorestring; if ( !ent->track ) { G_Error( "'trigger_objective_info' does not have a 'track' \n" ); } if ( level.numOidTriggers >= MAX_OID_TRIGGERS ) { G_Error( "Exceeded maximum number of 'trigger_objective_info' entities\n" ); } // JPW NERVE -- if this trigger has a "score" field set, then blowing up an objective // inside of this field will add "score" to the right player team. storing this // in ent->accuracy since that's unused. G_SpawnString( "score", "0", &scorestring ); ent->accuracy = atof( scorestring ); // jpw // Arnout: HACK HACK - someone at nerve forgot to add the score field to sub - have to // hardcode it cause we don't want people to download the map again { char mapName[MAX_QPATH]; trap_Cvar_VariableStringBuffer( "mapname", mapName, sizeof( mapName ) ); if ( !Q_stricmp( mapName, "mp_sub" ) && !Q_stricmp( ent->track, "the Axis Submarine" ) ) { ent->accuracy = 15; } } trap_SetConfigstring( CS_OID_TRIGGERS + level.numOidTriggers, ent->track ); ent->s.teamNum = level.numOidTriggers; level.numOidTriggers++; InitTrigger( ent ); // unlike other triggers, we need to send this one to the client ent->r.svFlags &= ~SVF_NOCLIENT; ent->s.eType = ET_OID_TRIGGER; trap_LinkEntity( ent ); // NERVE - SMF - spawn an explosive indicator if ( ( ent->spawnflags & AXIS_OBJECTIVE ) || ( ent->spawnflags & ALLIED_OBJECTIVE ) ) { gentity_t *e; e = G_Spawn(); e->r.svFlags = SVF_BROADCAST; e->classname = "explosive_indicator"; e->s.eType = ET_EXPLOSIVE_INDICATOR; e->s.pos.trType = TR_STATIONARY; if ( ent->spawnflags & AXIS_OBJECTIVE ) { e->s.teamNum = 1; } else if ( ent->spawnflags & ALLIED_OBJECTIVE ) { e->s.teamNum = 2; } e->r.ownerNum = ent->s.number; e->think = explosive_indicator_think; e->nextthink = level.time + FRAMETIME; VectorCopy( ent->r.mins, e->s.pos.trBase ); VectorAdd( ent->r.maxs, e->s.pos.trBase, e->s.pos.trBase ); VectorScale( e->s.pos.trBase, 0.5, e->s.pos.trBase ); SnapVector( e->s.pos.trBase ); trap_LinkEntity( e ); } // -NERVE - SMF }
/* ================== AICast_CheckLoadGame at the start of a level, the game is either saved, or loaded we must wait for all AI to spawn themselves, and a real client to connect ================== */ void AICast_CheckLoadGame( void ) { char loading[4]; gentity_t *ent = NULL; // TTimo: VC6 'may be used without having been init' qboolean ready; cast_state_t *pcs; // have we already done the save or load? if ( !saveGamePending ) { return; } // tell the cgame NOT to render the scene while we are waiting for things to settle trap_Cvar_Set( "cg_norender", "1" ); trap_Cvar_VariableStringBuffer( "savegame_loading", loading, sizeof( loading ) ); // reloading = qtrue; trap_Cvar_Set( "g_reloading", "1" ); if ( strlen( loading ) > 0 && atoi( loading ) != 0 ) { // screen should be black if we are at this stage trap_SetConfigstring( CS_SCREENFADE, va( "1 %i 1", level.time - 10 ) ); // if (!reloading && atoi(loading) == 2) { if ( !( g_reloading.integer ) && atoi( loading ) == 2 ) { // (SA) hmm, this seems redundant when it sets it above... // reloading = qtrue; // this gets reset at the Map_Restart() since the server unloads the game dll trap_Cvar_Set( "g_reloading", "1" ); } ready = qtrue; if ( numSpawningCast != numcast ) { ready = qfalse; } else if ( !( ent = AICast_FindEntityForName( "player" ) ) ) { ready = qfalse; } else if ( !ent->client || ent->client->pers.connected != CON_CONNECTED ) { ready = qfalse; } if ( ready ) { trap_Cvar_Set( "savegame_loading", "0" ); // in-case it aborts saveGamePending = qfalse; G_LoadGame( NULL ); // always load the "current" savegame // RF, spawn a thinker that will enable rendering after the client has had time to process the entities and setup the display //trap_Cvar_Set( "cg_norender", "0" ); ent = G_Spawn(); ent->nextthink = level.time + 200; ent->think = AICast_EnableRenderingThink; // wait for the clients to return from faded screen //trap_SetConfigstring( CS_SCREENFADE, va("0 %i 1500", level.time + 500) ); trap_SetConfigstring( CS_SCREENFADE, va( "0 %i 750", level.time + 500 ) ); level.reloadPauseTime = level.time + 1100; // make sure sound fades up trap_SendServerCommand( -1, va( "snd_fade 1 %d", 2000 ) ); //----(SA) added AICast_CastScriptThink(); } } else { ready = qtrue; if ( numSpawningCast != numcast ) { ready = qfalse; } else if ( !( ent = AICast_FindEntityForName( "player" ) ) ) { ready = qfalse; } else if ( !ent->client || ent->client->pers.connected != CON_CONNECTED ) { ready = qfalse; } // not loading a game, we must be in a new level, so look for some persistant data to read in, then save the game if ( ready ) { G_LoadPersistant(); // make sure we save the game after we have brought across the items trap_Cvar_Set( "g_totalPlayTime", "0" ); // reset play time trap_Cvar_Set( "g_attempts", "0" ); pcs = AICast_GetCastState( ent->s.number ); pcs->totalPlayTime = 0; pcs->lastLoadTime = 0; pcs->attempts = 0; // RF, disabled, since the pregame menu turns this off after the button is pressed, this isn't // required here // RF, spawn a thinker that will enable rendering after the client has had time to process the entities and setup the display //trap_Cvar_Set( "cg_norender", "0" ); //ent = G_Spawn(); //ent->nextthink = level.time + 200; //ent->think = AICast_EnableRenderingThink; saveGamePending = qfalse; // wait for the clients to return from faded screen // trap_SetConfigstring( CS_SCREENFADE, va("0 %i 1500", level.time + 500) ); // trap_SetConfigstring( CS_SCREENFADE, va("0 %i 750", level.time + 500) ); // (SA) send a command that will be interpreted for both the screenfade and any other effects (music cues, pregame menu, etc) // briefing menu will handle transition, just set a cvar for it to check for drawing the 'continue' button trap_SendServerCommand( -1, "rockandroll\n" ); level.reloadPauseTime = level.time + 1100; AICast_CastScriptThink(); } } }
/* =========== ClientDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropClient(), which will call this and do server system housekeeping. ============ */ void ClientDisconnect( int clientNum ) { gentity_t *ent; gentity_t *tent; int i; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( clientNum ); ent = g_entities + clientNum; if ( !ent->client ) { return; } // stop any following clients for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].sess.sessionTeam == TEAM_SPECTATOR && level.clients[i].sess.spectatorState == SPECTATOR_FOLLOW && level.clients[i].sess.spectatorClient == clientNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected if ( ent->client->pers.connected == CON_CONNECTED && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.clientNum = ent->s.clientNum; // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossClientItems( ent ); #ifdef MISSIONPACK TossClientPersistantPowerups( ent ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( ent ); } #endif } G_LogPrintf( "ClientDisconnect: %i\n", clientNum ); // if we are playing in tourney mode and losing, give a win to the other player if ( (g_gametype.integer == GT_TOURNAMENT ) && !level.intermissiontime && !level.warmupTime && level.sortedClients[1] == clientNum ) { level.clients[ level.sortedClients[0] ].sess.wins++; ClientUserinfoChanged( level.sortedClients[0] ); } trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->client->pers.connected = CON_DISCONNECTED; ent->client->ps.persistant[PERS_TEAM] = TEAM_FREE; ent->client->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + clientNum, ""); CalculateRanks(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownClient( clientNum, qfalse ); } }
/* =========== PlayerUserinfoChanged Called from PlayerConnect 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. ============ */ void PlayerUserinfoChanged( int playerNum ) { gentity_t *ent; int teamTask, teamLeader; char *s; char model[MAX_QPATH]; char headModel[MAX_QPATH]; char oldname[MAX_STRING_CHARS]; gplayer_t *player; char c1[MAX_INFO_STRING]; char c2[MAX_INFO_STRING]; char userinfo[MAX_INFO_STRING]; ent = g_entities + playerNum; player = ent->player; trap_GetUserinfo( playerNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if ( !Info_Validate(userinfo) ) { strcpy (userinfo, "\\name\\badinfo"); // don't keep those players and userinfo trap_DropPlayer(playerNum, "Invalid userinfo"); } // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if ( !atoi( s ) ) { player->pers.predictItemPickup = qfalse; } else { player->pers.predictItemPickup = qtrue; } // check the anti lag s = Info_ValueForKey( userinfo, "cg_antiLag" ); player->pers.antiLag = atoi( s ); // set name Q_strncpyz ( oldname, player->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey (userinfo, "name"); PlayerCleanName( s, player->pers.netname, sizeof(player->pers.netname) ); if ( player->sess.sessionTeam == TEAM_SPECTATOR ) { if ( player->sess.spectatorState == SPECTATOR_SCOREBOARD ) { Q_strncpyz( player->pers.netname, "scoreboard", sizeof(player->pers.netname) ); } } if ( player->pers.connected == CON_CONNECTED ) { if ( strcmp( oldname, player->pers.netname ) ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, player->pers.netname) ); } } // set max health #ifdef MISSIONPACK if (player->ps.powerups[PW_GUARD]) { player->pers.maxHealth = 200; } else { player->pers.maxHealth = PlayerHandicap( player ); } #else player->pers.maxHealth = PlayerHandicap( player ); #endif player->ps.stats[STAT_MAX_HEALTH] = player->pers.maxHealth; // set model if( g_gametype.integer >= GT_TEAM ) { Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) ); } else { Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); } #ifdef MISSIONPACK if (g_gametype.integer >= GT_TEAM) { player->pers.teamInfo = qtrue; } else { s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { player->pers.teamInfo = qtrue; } else { player->pers.teamInfo = qfalse; } } #else // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { player->pers.teamInfo = qtrue; } else { player->pers.teamInfo = qfalse; } #endif /* s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); if ( !*s || atoi( s ) == 0 ) { player->pers.pmoveFixed = qfalse; } else { player->pers.pmoveFixed = qtrue; } */ // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi(Info_ValueForKey(userinfo, "teamtask")); // team Leader (1 = leader, 0 is normal player) teamLeader = player->sess.teamLeader; // colors strcpy(c1, Info_ValueForKey( userinfo, "color1" )); strcpy(c2, Info_ValueForKey( userinfo, "color2" )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds if (ent->r.svFlags & SVF_BOT) { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", player->pers.netname, player->sess.sessionTeam, model, headModel, c1, c2, player->pers.maxHealth, player->sess.wins, player->sess.losses, Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); } else { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", player->pers.netname, player->sess.sessionTeam, model, headModel, c1, c2, player->pers.maxHealth, player->sess.wins, player->sess.losses, teamTask, teamLeader); } trap_SetConfigstring( CS_PLAYERS+playerNum, s ); // this is not the userinfo, more like the configstring actually G_LogPrintf( "PlayerUserinfoChanged: %i %s\n", playerNum, s ); }
/*QUAKED worldspawn (0 0 0) ? NO_GT_WOLF NO_GT_STOPWATCH NO_GT_CHECKPOINT NO_LMS Every map should have exactly one worldspawn. "music" Music wav file "gravity" 800 is default gravity "message" Text to print during connection process "ambient" Ambient light value (must use '_color') "_color" Ambient light color (must be used with 'ambient') "sun" Shader to use for 'sun' image */ void SP_worldspawn(void) { char *s; G_SpawnString("classname", "", &s); if (Q_stricmp(s, "worldspawn")) { G_Error("SP_worldspawn: The first entity isn't 'worldspawn'\n"); } // make some data visible to connecting client trap_SetConfigstring(CS_GAME_VERSION, GAME_VERSION); trap_SetConfigstring(CS_LEVEL_START_TIME, va("%i", level.startTime)); G_SpawnString("music", "", &s); trap_SetConfigstring(CS_MUSIC, s); G_SpawnString("message", "", &s); trap_SetConfigstring(CS_MESSAGE, s); // map specific message G_SpawnString("cclayers", "0", &s); if (atoi(s)) { level.ccLayers = qtrue; } level.mapcoordsValid = qfalse; if (G_SpawnVector2D("mapcoordsmins", "-128 128", level.mapcoordsMins) && // top left G_SpawnVector2D("mapcoordsmaxs", "128 -128", level.mapcoordsMaxs)) // bottom right { level.mapcoordsValid = qtrue; } BG_InitLocations(level.mapcoordsMins, level.mapcoordsMaxs); trap_SetConfigstring(CS_MOTD, g_motd.string); // message of the day G_SpawnString("gravity", "800", &s); trap_Cvar_Set("g_gravity", s); G_SpawnString("spawnflags", "0", &s); g_entities[ENTITYNUM_WORLD].spawnflags = atoi(s); g_entities[ENTITYNUM_WORLD].r.worldflags = g_entities[ENTITYNUM_WORLD].spawnflags; g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD; g_entities[ENTITYNUM_WORLD].r.ownerNum = ENTITYNUM_NONE; g_entities[ENTITYNUM_WORLD].classname = "worldspawn"; g_entities[ENTITYNUM_NONE].s.number = ENTITYNUM_NONE; g_entities[ENTITYNUM_NONE].r.ownerNum = ENTITYNUM_NONE; g_entities[ENTITYNUM_NONE].classname = "nothing"; // see if we want a warmup time trap_SetConfigstring(CS_WARMUP, ""); if (g_restarted.integer) { trap_Cvar_Set("g_restarted", "0"); level.warmupTime = 0; } if (g_gamestate.integer == GS_PLAYING) { G_initMatch(); } }
/* ============ CalculateRanks Recalculates the score ranks of all players This will be called on every client connect, begin, disconnect, death, and team change. ============ */ void CalculateRanks( void ) { int i; int rank; int score; int newScore; gclient_t *cl; level.follow1 = -1; level.follow2 = -1; level.numConnectedClients = 0; level.numNonSpectatorClients = 0; level.numPlayingClients = 0; level.numVotingClients = 0; // don't count bots for ( i = 0; i < TEAM_NUM_TEAMS; i++ ) { level.numteamVotingClients[i] = 0; } for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected != CON_DISCONNECTED ) { level.sortedClients[level.numConnectedClients] = i; level.numConnectedClients++; if ( level.clients[i].sess.sessionTeam != TEAM_SPECTATOR ) { level.numNonSpectatorClients++; // decide if this should be auto-followed if ( level.clients[i].pers.connected == CON_CONNECTED ) { level.numPlayingClients++; if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { level.numVotingClients++; if ( level.clients[i].sess.sessionTeam == TEAM_RED ) level.numteamVotingClients[0]++; else if ( level.clients[i].sess.sessionTeam == TEAM_BLUE ) level.numteamVotingClients[1]++; } if ( level.follow1 == -1 ) { level.follow1 = i; } else if ( level.follow2 == -1 ) { level.follow2 = i; } } } } } qsort( level.sortedClients, level.numConnectedClients, sizeof(level.sortedClients[0]), SortRanks ); // set the rank value for all clients that are connected and not spectators if ( g_gametype.integer >= GT_TEAM ) { // in team games, rank is just the order of the teams, 0=red, 1=blue, 2=tied for ( i = 0; i < level.numConnectedClients; i++ ) { cl = &level.clients[ level.sortedClients[i] ]; if ( level.teamScores[TEAM_RED] == level.teamScores[TEAM_BLUE] ) { cl->ps.persistant[PERS_RANK] = 2; } else if ( level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE] ) { cl->ps.persistant[PERS_RANK] = 0; } else { cl->ps.persistant[PERS_RANK] = 1; } } } else { rank = -1; score = 0; for ( i = 0; i < level.numPlayingClients; i++ ) { cl = &level.clients[ level.sortedClients[i] ]; newScore = cl->ps.persistant[PERS_SCORE]; if ( i == 0 || newScore != score ) { rank = i; // assume we aren't tied until the next client is checked level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank; } else { // we are tied with the previous client level.clients[ level.sortedClients[i-1] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; } score = newScore; if ( g_gametype.integer == GT_SINGLE_PLAYER && level.numPlayingClients == 1 ) { level.clients[ level.sortedClients[i] ].ps.persistant[PERS_RANK] = rank | RANK_TIED_FLAG; } } } // set the CS_SCORES1/2 configstrings, which will be visible to everyone if ( g_gametype.integer >= GT_TEAM ) { trap_SetConfigstring( CS_SCORES1, va("%i", level.teamScores[TEAM_RED] ) ); trap_SetConfigstring( CS_SCORES2, va("%i", level.teamScores[TEAM_BLUE] ) ); } else { if ( level.numConnectedClients == 0 ) { trap_SetConfigstring( CS_SCORES1, va("%i", SCORE_NOT_PRESENT) ); trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) ); } else if ( level.numConnectedClients == 1 ) { trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) ); trap_SetConfigstring( CS_SCORES2, va("%i", SCORE_NOT_PRESENT) ); } else { trap_SetConfigstring( CS_SCORES1, va("%i", level.clients[ level.sortedClients[0] ].ps.persistant[PERS_SCORE] ) ); trap_SetConfigstring( CS_SCORES2, va("%i", level.clients[ level.sortedClients[1] ].ps.persistant[PERS_SCORE] ) ); } } // see if it is time to end the level CheckExitRules(); // if we are at the intermission, send the new info to everyone if ( level.intermissiontime ) { SendScoreboardMessageToAllClients(); } }
/* =========== PlayerDisconnect Called when a player drops from the server. Will not be called between levels. This should NOT be called directly by any game logic, call trap_DropPlayer(), which will call this and do server system housekeeping. ============ */ void PlayerDisconnect( int playerNum ) { gentity_t *ent; gentity_t *tent; int i; // cleanup if we are kicking a bot that // hasn't spawned yet G_RemoveQueuedBotBegin( playerNum ); ent = g_entities + playerNum; if (!ent->player || ent->player->pers.connected == CON_DISCONNECTED) { return; } // stop any following players for ( i = 0 ; i < level.maxplayers ; i++ ) { if ( level.players[i].sess.sessionTeam == TEAM_SPECTATOR && level.players[i].sess.spectatorState == SPECTATOR_FOLLOW && level.players[i].sess.spectatorPlayer == playerNum ) { StopFollowing( &g_entities[i] ); } } // send effect if they were completely connected if ( ent->player->pers.connected == CON_CONNECTED && ent->player->sess.sessionTeam != TEAM_SPECTATOR ) { tent = G_TempEntity( ent->player->ps.origin, EV_PLAYER_TELEPORT_OUT ); tent->s.playerNum = ent->s.playerNum; // They don't get to take powerups with them! // Especially important for stuff like CTF flags TossPlayerItems( ent ); #ifdef MISSIONPACK TossPlayerPersistantPowerups( ent ); if( g_gametype.integer == GT_HARVESTER ) { TossPlayerCubes( ent ); } #endif } G_LogPrintf( "PlayerDisconnect: %i\n", playerNum ); // if we are playing in tourney mode and losing, give a win to the other player if ( (g_gametype.integer == GT_TOURNAMENT ) && !level.intermissiontime && !level.warmupTime && level.sortedPlayers[1] == playerNum ) { level.players[ level.sortedPlayers[0] ].sess.wins++; PlayerUserinfoChanged( level.sortedPlayers[0] ); } if( g_gametype.integer == GT_TOURNAMENT && ent->player->sess.sessionTeam == TEAM_FREE && level.intermissiontime ) { trap_Cmd_ExecuteText( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; level.changemap = NULL; level.intermissiontime = 0; } trap_UnlinkEntity (ent); ent->s.modelindex = 0; ent->inuse = qfalse; ent->classname = "disconnected"; ent->player->pers.connected = CON_DISCONNECTED; ent->player->ps.persistant[PERS_TEAM] = TEAM_FREE; ent->player->sess.sessionTeam = TEAM_FREE; trap_SetConfigstring( CS_PLAYERS + playerNum, ""); CalculateRanks(); if ( ent->r.svFlags & SVF_BOT ) { BotAIShutdownPlayer( playerNum, qfalse ); } // clear player connection info level.connections[ent->player->pers.connectionNum].numLocalPlayers--; level.connections[ent->player->pers.connectionNum].localPlayerNums[ent->player->pers.localPlayerNum] = -1; ent->player->pers.localPlayerNum = ent->player->pers.connectionNum = -1; }
/* ============= CheckTournament Once a frame, check for changes in tournement player state ============= */ void CheckTournament( void ) { // check because we run 3 game frames before calling Connect and/or ClientBegin // for clients on a map_restart if ( level.numPlayingClients == 0 ) { return; } if ( g_gametype.integer == GT_TOURNAMENT ) { // pull in a spectator if needed if ( level.numPlayingClients < 2 ) { AddTournamentPlayer(); } // if we don't have two players, go back to "waiting for players" if ( level.numPlayingClients != 2 ) { if ( level.warmupTime != -1 ) { level.warmupTime = -1; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); G_LogPrintf( "Warmup:\n" ); } return; } if ( level.warmupTime == 0 ) { return; } // if the warmup is changed at the console, restart it if ( g_warmup.modificationCount != level.warmupModificationCount ) { level.warmupModificationCount = g_warmup.modificationCount; level.warmupTime = -1; } // if all players have arrived, start the countdown if ( level.warmupTime < 0 ) { if ( level.numPlayingClients == 2 ) { // fudge by -1 to account for extra delays if ( g_warmup.integer > 1 ) { level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000; } else { level.warmupTime = 0; } trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); } return; } // if the warmup time has counted down, restart if ( level.time > level.warmupTime ) { level.warmupTime += 10000; trap_Cvar_Set( "g_restarted", "1" ); trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; return; } } else if ( g_gametype.integer != GT_SINGLE_PLAYER && level.warmupTime != 0 ) { int counts[TEAM_NUM_TEAMS]; qboolean notEnough = qfalse; if ( g_gametype.integer > GT_TEAM ) { counts[TEAM_BLUE] = TeamCount( -1, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( -1, TEAM_RED ); if (counts[TEAM_RED] < 1 || counts[TEAM_BLUE] < 1) { notEnough = qtrue; } } else if ( level.numPlayingClients < 2 ) { notEnough = qtrue; } if ( notEnough ) { if ( level.warmupTime != -1 ) { level.warmupTime = -1; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); G_LogPrintf( "Warmup:\n" ); } return; // still waiting for team members } if ( level.warmupTime == 0 ) { return; } // if the warmup is changed at the console, restart it if ( g_warmup.modificationCount != level.warmupModificationCount ) { level.warmupModificationCount = g_warmup.modificationCount; level.warmupTime = -1; } // if all players have arrived, start the countdown if ( level.warmupTime < 0 ) { // fudge by -1 to account for extra delays level.warmupTime = level.time + ( g_warmup.integer - 1 ) * 1000; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); return; } // if the warmup time has counted down, restart if ( level.time > level.warmupTime ) { level.warmupTime += 10000; trap_Cvar_Set( "g_restarted", "1" ); trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" ); level.restarted = qtrue; return; } } }
/* ================ LogExit Append information about this game to the log file ================ */ void LogExit( const char *string ) { int i, numSorted; gclient_t *cl; #ifdef MISSIONPACK qboolean won = qtrue; #endif G_LogPrintf( "Exit: %s\n", string ); level.intermissionQueued = level.time; // this will keep the clients from playing any voice sounds // that will get cut off when the queued intermission starts trap_SetConfigstring( CS_INTERMISSION, "1" ); // don't send more than 32 scores (FIXME?) numSorted = level.numConnectedClients; if ( numSorted > 32 ) { numSorted = 32; } if ( g_gametype.integer >= GT_TEAM ) { G_LogPrintf( "red:%i blue:%i\n", level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE] ); } for (i=0 ; i < numSorted ; i++) { int ping; cl = &level.clients[level.sortedClients[i]]; if ( cl->sess.sessionTeam == TEAM_SPECTATOR ) { continue; } if ( cl->pers.connected == CON_CONNECTING ) { continue; } ping = cl->ps.ping < 999 ? cl->ps.ping : 999; G_LogPrintf( "score: %i ping: %i client: %i %s\n", cl->ps.persistant[PERS_SCORE], ping, level.sortedClients[i], cl->pers.netname ); #ifdef MISSIONPACK if (g_singlePlayer.integer && g_gametype.integer == GT_TOURNAMENT) { if (g_entities[cl - level.clients].r.svFlags & SVF_BOT && cl->ps.persistant[PERS_RANK] == 0) { won = qfalse; } } #endif } #ifdef MISSIONPACK if (g_singlePlayer.integer) { if (g_gametype.integer >= GT_CTF) { won = level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]; } trap_SendConsoleCommand( EXEC_APPEND, (won) ? "spWin\n" : "spLose\n" ); } #endif }
void target_play_music_use(gentity_t *self, gentity_t *other, gentity_t *activator) { G_ActivateBehavior(self,BSET_USE); trap_SetConfigstring( CS_MUSIC, self->message ); }
// ************** PAUSE / UNPAUSE // // Pause/unpause a match. void G_pause_cmd(gentity_t * ent, unsigned int dwCommand, qboolean fPause) { char *status[2] = { "^5UN", "^1" }; if(team_nocontrols.integer) { G_noTeamControls(ent); return; } if((PAUSE_UNPAUSING >= level.match_pause && !fPause) || (PAUSE_NONE != level.match_pause && fPause)) { CP(va("print \"The match is already %sPAUSED^7!\n\"", status[fPause])); return; } // Alias for referees if(ent->client->sess.referee) { G_refPause_cmd(ent, fPause); } else { int tteam = G_teamID(ent); if(!G_cmdDebounce(ent, aCommandInfo[dwCommand].pszCommandName)) { return; } // Trigger the auto-handling of pauses if(fPause) { if(0 == teamInfo[tteam].timeouts) { CP("cpm \"^3Your team has no more timeouts remaining!\n\""); return; } else { teamInfo[tteam].timeouts--; level.match_pause = tteam + 128; G_globalSound("sound/misc/referee.wav"); G_spawnPrintf(DP_PAUSEINFO, level.time + 15000, NULL); AP(va ("print \"^3Match is ^1PAUSED^3!\n^7[%s^7: - %d Timeouts Remaining]\n\"", aTeams[tteam], teamInfo[tteam].timeouts)); AP(va("cp \"^3Match is ^1PAUSED^3! (%s^3)\n\"", aTeams[tteam])); // CHRUKER: b040 - Was only sending this to the client sending the command level.server_settings |= CV_SVS_PAUSE; trap_SetConfigstring(CS_SERVERTOGGLES, va("%d", level.server_settings)); } } else if(tteam + 128 != level.match_pause) { CP("cpm \"^3Your team didn't call the timeout!\n\""); return; } else { AP("print \"^3Match is ^5UNPAUSED^3 ... resuming in 10 seconds!\n\""); // CHRUKER: b068 - Had extra linebreaks, before and after. level.match_pause = PAUSE_UNPAUSING; G_globalSound("sound/osp/prepare.wav"); G_spawnPrintf(DP_UNPAUSING, level.time + 10, NULL); } } }
/* ================== Cmd_CallVote_f ================== */ void Cmd_CallVote_f( gentity_t *ent ) { char* c; int i; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; if ( !g_allowVote.integer ) { trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" ); return; } if ( level.voteTime ) { trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" ); return; } if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) { trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" ); return; } if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" ); return; } // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); trap_Argv( 2, arg2, sizeof( arg2 ) ); // check for command separators in arg2 for( c = arg2; *c; ++c) { switch(*c) { case '\n': case '\r': case ';': trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); return; break; } } if ( !Q_stricmp( arg1, "map_restart" ) ) { } else if ( !Q_stricmp( arg1, "nextmap" ) ) { } else if ( !Q_stricmp( arg1, "map" ) ) { } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { } else if ( !Q_stricmp( arg1, "kick" ) ) { } else if ( !Q_stricmp( arg1, "clientkick" ) ) { } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) { } else if ( !Q_stricmp( arg1, "timelimit" ) ) { } else if ( !Q_stricmp( arg1, "fraglimit" ) ) { } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map <mapname>, g_gametype <n>, kick <player>, clientkick <clientnum>, g_doWarmup, timelimit <time>, fraglimit <frags>.\n\"" ); return; } // if there is still a vote to be executed if ( level.voteExecuteTime ) { level.voteExecuteTime = 0; trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) ); } // special case for g_gametype, check for bad values if ( !Q_stricmp( arg1, "g_gametype" ) ) { i = atoi( arg2 ); if( i == GT_SINGLE_PLAYER || i < GT_FFA || i >= GT_MAX_GAME_TYPE) { trap_SendServerCommand( ent-g_entities, "print \"Invalid gametype.\n\"" ); return; } Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %d", arg1, i ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, gameNames[i] ); } else if ( !Q_stricmp( arg1, "map" ) ) { // special case for map changes, we want to reset the nextmap setting // this allows a player to change maps, but not upset the map rotation char s[MAX_STRING_CHARS]; trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) ); if (*s) { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s ); } else { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 ); } Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); } else if ( !Q_stricmp( arg1, "nextmap" ) ) { char s[MAX_STRING_CHARS]; trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) ); if (!*s) { trap_SendServerCommand( ent-g_entities, "print \"nextmap not set.\n\"" ); return; } Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap"); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); } else { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); } trap_SendServerCommand( -1, va("print \"%s called a vote.\n\"", ent->client->pers.netname ) ); // start the voting, the caller autoamtically votes yes level.voteTime = level.time; level.voteYes = 1; level.voteNo = 0; for ( i = 0 ; i < level.maxclients ; i++ ) { level.clients[i].ps.eFlags &= ~EF_VOTED; } ent->client->ps.eFlags |= EF_VOTED; trap_SetConfigstring( CS_VOTE_TIME, va("%i", level.voteTime ) ); trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString ); trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) ); trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) ); }
void G_configLoadAndSet(const char *name) { pc_token_t token; int handle; config_t *config; qboolean parseOK = qtrue; handle = trap_PC_LoadSource(va("configs/%s.config", name)); if (!handle) { Com_Printf(S_COLOR_RED "ERROR: File not found: %s\n", name); return; } memset(&level.config, 0, sizeof(config_t)); G_wipeCvars(); config = &level.config; config->publicConfig = qfalse; while (1) { if (!trap_PC_ReadToken(handle, &token)) { break; } if (!Q_stricmp(token.string, "configname")) { if (!PC_String_ParseNoAlloc(handle, config->name, sizeof(config->name))) { G_Printf("expected config name\n"); parseOK = qfalse; break; } G_Printf("Config name is: %s\n", config->name); } else if (!Q_stricmp(token.string, "version")) { if (!PC_String_ParseNoAlloc(handle, config->version, sizeof(config->name))) { G_Printf("expected config version\n"); parseOK = qfalse; break; } } else if (!Q_stricmp(token.string, "init")) { if (!G_ParseSettings(handle, qtrue, config)) { G_Printf("Reading settings failed\n"); parseOK = qfalse; break; } } else if (!Q_stricmp(token.string, "map")) { if (!G_ParseMapSettings(handle, config)) { G_Printf("Reading map settings failed\n"); parseOK = qfalse; break; } } else if (!Q_stricmp(token.string, "signature")) { if (!PC_String_ParseNoAlloc(handle, config->signature, sizeof(config->signature))) { G_Printf("expected config signature\n"); parseOK = qfalse; break; } } else if (!Q_stricmp(token.string, "public")) { config->publicConfig = qtrue; } else { G_Printf("unknown token %s\n", token.string); parseOK = qfalse; break; } } trap_PC_FreeSource(handle); if (parseOK) { trap_SetConfigstring(CS_CONFIGNAME, config->name); if (level.config.version[0] && level.config.name[0]) { trap_SendServerCommand(-1, va("cp \"^7Config '%s^7' version '%s'^7 loaded\"", level.config.name, level.config.version)); } else if (level.config.name[0]) { trap_SendServerCommand(-1, va("cp \"^7Config '%s^7' loaded\"", level.config.name)); } } else { trap_SetConfigstring(CS_CONFIGNAME, ""); trap_SendServerCommand(-1, va("cp \"^7Config '%s^7' ^1FAILED ^7to load\"", name)); } G_UpdateCvars(); }
/* ================== Cmd_CallTeamVote_f ================== */ void Cmd_CallTeamVote_f( gentity_t *ent ) { int i, team, cs_offset; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; team = ent->client->sess.sessionTeam; if ( team == TEAM_RED ) cs_offset = 0; else if ( team == TEAM_BLUE ) cs_offset = 1; else return; if ( !g_allowVote.integer ) { trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" ); return; } if ( level.teamVoteTime[cs_offset] ) { trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress.\n\"" ); return; } if ( ent->client->pers.teamVoteCount >= MAX_VOTE_COUNT ) { trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of team votes.\n\"" ); return; } if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" ); return; } // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); arg2[0] = '\0'; for ( i = 2; i < trap_Argc(); i++ ) { if (i > 2) strcat(arg2, " "); trap_Argv( i, &arg2[strlen(arg2)], sizeof( arg2 ) - strlen(arg2) ); } if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); return; } if ( !Q_stricmp( arg1, "leader" ) ) { char netname[MAX_NETNAME], leader[MAX_NETNAME]; if ( !arg2[0] ) { i = ent->client->ps.clientNum; } else { // numeric values are just slot numbers for (i = 0; i < 3; i++) { if ( !arg2[i] || arg2[i] < '0' || arg2[i] > '9' ) break; } if ( i >= 3 || !arg2[i]) { i = atoi( arg2 ); if ( i < 0 || i >= level.maxclients ) { trap_SendServerCommand( ent-g_entities, va("print \"Bad client slot: %i\n\"", i) ); return; } if ( !g_entities[i].inuse ) { trap_SendServerCommand( ent-g_entities, va("print \"Client %i is not active\n\"", i) ); return; } } else { Q_strncpyz(leader, arg2, sizeof(leader)); Q_CleanStr(leader); for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected == CON_DISCONNECTED ) continue; if (level.clients[i].sess.sessionTeam != team) continue; Q_strncpyz(netname, level.clients[i].pers.netname, sizeof(netname)); Q_CleanStr(netname); if ( !Q_stricmp(netname, leader) ) { break; } } if ( i >= level.maxclients ) { trap_SendServerCommand( ent-g_entities, va("print \"%s is not a valid player on your team.\n\"", arg2) ); return; } } } Com_sprintf(arg2, sizeof(arg2), "%d", i); } else { trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); trap_SendServerCommand( ent-g_entities, "print \"Team vote commands are: leader <player>.\n\"" ); return; } Com_sprintf( level.teamVoteString[cs_offset], sizeof( level.teamVoteString[cs_offset] ), "%s %s", arg1, arg2 ); for ( i = 0 ; i < level.maxclients ; i++ ) { if ( level.clients[i].pers.connected == CON_DISCONNECTED ) continue; if (level.clients[i].sess.sessionTeam == team) trap_SendServerCommand( i, va("print \"%s called a team vote.\n\"", ent->client->pers.netname ) ); } // start the voting, the caller autoamtically votes yes level.teamVoteTime[cs_offset] = level.time; level.teamVoteYes[cs_offset] = 1; level.teamVoteNo[cs_offset] = 0; for ( i = 0 ; i < level.maxclients ; i++ ) { if (level.clients[i].sess.sessionTeam == team) level.clients[i].ps.eFlags &= ~EF_TEAMVOTED; } ent->client->ps.eFlags |= EF_TEAMVOTED; trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, va("%i", level.teamVoteTime[cs_offset] ) ); trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, level.teamVoteString[cs_offset] ); trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) ); trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) ); }
/*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY RESURECTABLE COMPASS ALLIED AXIS MOUNTED_GUN Scripted brush entity. A simplified means of moving brushes around based on events. "modelscale" - Scale multiplier (defaults to 1, and scales uniformly) "modelscale_vec" - Set scale per-axis. Overrides "modelscale", so if you have both the "modelscale" is ignored "model2" optional md3 to draw over the solid clip brush "scriptname" name used for scripting purposes (like aiName in AI scripting) "health" optionally make this entity damagable "description" used with health, if the entity is damagable, it draws a healthbar with this description above it. */ void SP_script_mover(gentity_t *ent) { float scale[3] = {1,1,1}; vec3_t scalevec; char tagname[MAX_QPATH]; char* modelname; char* tagent; char cs[MAX_INFO_STRING]; char* s; if (!ent->model) G_Error("script_mover must have a \"model\"\n" ); if (!ent->scriptName) G_Error("script_mover must have a \"scriptname\"\n" ); ent->blocked = script_mover_blocked; // first position at start VectorCopy( ent->s.origin, ent->pos1 ); // VectorCopy( ent->r.currentOrigin, ent->pos1 ); VectorCopy( ent->pos1, ent->pos2 ); // don't go anywhere just yet trap_SetBrushModel( ent, ent->model ); InitMover( ent ); ent->reached = NULL; ent->s.animMovetype = 0; ent->s.density = 0; if (ent->spawnflags & 256) { ent->s.density |= 2; } if (ent->spawnflags & 8) { ent->use = script_mover_use; } if (ent->spawnflags & 16) { ent->s.time2 = 1; } else { ent->s.time2 = 0; } if (ent->spawnflags & 32) { ent->s.teamNum = TEAM_ALLIES; } else if (ent->spawnflags & 64) { ent->s.teamNum = TEAM_AXIS; } else { ent->s.teamNum = TEAM_FREE; } if (ent->spawnflags & 1) { ent->use = script_mover_use; trap_UnlinkEntity(ent); // make sure it's not visible return; } G_SetAngle (ent, ent->s.angles); G_SpawnInt( "health", "0", &ent->health ); if(ent->health) { ent->takedamage = qtrue; ent->count = ent->health; // client needs to know about it as well ent->s.effect1Time = ent->count; ent->s.dl_intensity = 255; if( G_SpawnString( "description", "", &s ) ) { trap_GetConfigstring( CS_SCRIPT_MOVER_NAMES, cs, sizeof(cs) ); Info_SetValueForKey( cs, va("%i",ent-g_entities), s ); trap_SetConfigstring( CS_SCRIPT_MOVER_NAMES, cs ); } } else { ent->count = 0; } ent->die = script_mover_die; // look for general scaling if(G_SpawnFloat( "modelscale", "1", &scale[0])) { scale[2] = scale[1] = scale[0]; } if(G_SpawnString( "model2", "", &modelname ) ) { COM_StripExtension( modelname, tagname ); Q_strcat( tagname, MAX_QPATH, ".tag" ); ent->tagNumber = trap_LoadTag( tagname ); /* if( !(ent->tagNumber = trap_LoadTag( tagname )) ) { Com_Error( ERR_DROP, "Failed to load Tag File (%s)\n", tagname ); }*/ } // look for axis specific scaling if(G_SpawnVector("modelscale_vec", "1 1 1", &scalevec[0])) { VectorCopy(scalevec, scale); } if(scale[0] != 1 || scale[1] != 1 || scale[2] != 1) { ent->s.density |= 1; // scale is stored in 'angles2' VectorCopy(scale, ent->s.angles2); } if (ent->spawnflags & 128) { ent->s.density |= 4; ent->waterlevel = 0; if( G_SpawnString( "gun", "", &modelname ) ) { if( !Q_stricmp( modelname, "browning" ) ) { ent->s.density |= 8; } } G_SpawnString("tagent", "", &tagent); Q_strncpyz( ent->tagBuffer, tagent, 16 ); ent->s.powerups = -1; } ent->think = script_mover_spawn; ent->nextthink = level.time + FRAMETIME; }
/*QUAKED worldspawn (0 0 0) ? Used for game-world global options. Every map should have exactly one. === KEYS === ; message: Text to print during connection process. Used for the name of level. ; music: path/name of looping .wav file used for level's music (eg. music/sonic5.wav). ; gravity: level gravity [g_gravity (800)] ; humanBuildPoints: maximum amount of power the humans can use. [g_humanBuildPoints] ; humanRepeaterBuildPoints: maximum amount of power the humans can use around each repeater. [g_humanRepeaterBuildPoints] ; alienBuildPoints: maximum amount of sentience available to the overmind. [g_alienBuildPoints] ; disabledEquipment: A comma delimited list of human weapons or upgrades to disable for this map. [g_disabledEquipment ()] ; disabledClasses: A comma delimited list of alien classes to disable for this map. [g_disabledClasses ()] ; disabledBuildables: A comma delimited list of buildables to disable for this map. [g_disabledBuildables ()] */ void SP_worldspawn() { char *s; float reverbIntensity = 1.0f; G_SpawnString( "classname", "", &s ); if ( Q_stricmp( s, S_WORLDSPAWN ) ) { Com_Error(errorParm_t::ERR_DROP, "SP_worldspawn: The first entry in the spawn string isn't of expected type '" S_WORLDSPAWN "'" ); } // make some data visible to connecting client trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION ); trap_SetConfigstring( CS_LEVEL_START_TIME, va( "%i", level.startTime ) ); G_SpawnString( "music", "", &s ); trap_SetConfigstring( CS_MUSIC, s ); G_SpawnString( "message", "", &s ); trap_SetConfigstring( CS_MESSAGE, s ); // map specific message if(G_SpawnString( "gradingTexture", "", &s )) trap_SetConfigstring( CS_GRADING_TEXTURES, va( "%i %f %s", -1, 0.0f, s ) ); if(G_SpawnString( "colorGrade", "", &s )) { Log::Warn("\"colorGrade\" deprecated. Please use \"gradingTexture\""); trap_SetConfigstring( CS_GRADING_TEXTURES, va( "%i %f %s", -1, 0.0f, s ) ); } if(G_SpawnString( "reverbIntensity", "", &s )) sscanf( s, "%f", &reverbIntensity ); if(G_SpawnString( "reverbEffect", "", &s )) trap_SetConfigstring( CS_REVERB_EFFECTS, va( "%i %f %s %f", 0, 0.0f, s, Com_Clamp( 0.0f, 2.0f, reverbIntensity ) ) ); trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day G_SpawnStringIntoCVarIfSet( "gravity", "g_gravity" ); G_SpawnStringIntoCVarIfSet( "humanBuildPoints", "g_humanBuildPoints" ); G_SpawnStringIntoCVarIfSet( "humanRepeaterBuildPoints", "g_humanRepeaterBuildPoints" ); G_SpawnStringIntoCVarIfSet( "alienBuildPoints", "g_alienBuildPoints" ); G_SpawnStringIntoCVar( "disabledEquipment", "g_disabledEquipment" ); G_SpawnStringIntoCVar( "disabledClasses", "g_disabledClasses" ); G_SpawnStringIntoCVar( "disabledBuildables", "g_disabledBuildables" ); g_entities[ ENTITYNUM_WORLD ].s.number = ENTITYNUM_WORLD; g_entities[ ENTITYNUM_WORLD ].r.ownerNum = ENTITYNUM_NONE; g_entities[ ENTITYNUM_WORLD ].classname = S_WORLDSPAWN; g_entities[ ENTITYNUM_NONE ].s.number = ENTITYNUM_NONE; g_entities[ ENTITYNUM_NONE ].r.ownerNum = ENTITYNUM_NONE; g_entities[ ENTITYNUM_NONE ].classname = "nothing"; // see if we want a warmup time trap_SetConfigstring( CS_WARMUP, "-1" ); if ( g_doWarmup.integer ) { level.warmupTime = level.matchTime + ( g_warmup.integer * 1000 ); trap_SetConfigstring( CS_WARMUP, va( "%i", level.warmupTime ) ); G_LogPrintf( "Warmup: %i\n", g_warmup.integer ); } level.timelimit = g_timelimit.integer; }
/* =========== 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. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int teamTask, teamLeader, team, health; char *s; char model[MAX_QPATH]; char headModel[MAX_QPATH]; char oldname[MAX_STRING_CHARS]; gclient_t *client; char c1[MAX_INFO_STRING]; char c2[MAX_INFO_STRING]; char redTeam[MAX_INFO_STRING]; char blueTeam[MAX_INFO_STRING]; 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) ) { strcpy (userinfo, "\\name\\badinfo"); } // check for local client s = Info_ValueForKey( userinfo, "ip" ); if ( !strcmp( s, "localhost" ) ) { client->pers.localClient = qtrue; } // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if ( !atoi( s ) ) { client->pers.predictItemPickup = qfalse; } else { client->pers.predictItemPickup = qtrue; } // set name Q_strncpyz ( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey (userinfo, "name"); ClientCleanName( s, client->pers.netname, sizeof(client->pers.netname) ); if ( client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) { Q_strncpyz( client->pers.netname, "scoreboard", sizeof(client->pers.netname) ); } } if ( client->pers.connected == CON_CONNECTED ) { if ( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname) ); } } // set max health #ifdef MISSIONPACK if (client->ps.powerups[PW_GUARD]) { client->pers.maxHealth = 200; } else { health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } } #else health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) { client->pers.maxHealth = 100; } #endif client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // set model if( g_gametype.integer >= GT_TEAM ) { Q_strncpyz( model, Info_ValueForKey (userinfo, "team_model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "team_headmodel"), sizeof( headModel ) ); } else { Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) ); Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); } // bots set their team a few frames later if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) { s = Info_ValueForKey( userinfo, "team" ); if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } } else { team = client->sess.sessionTeam; } /* NOTE: all client side now // team switch( team ) { case TEAM_RED: ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); break; case TEAM_BLUE: ForceClientSkin(client, model, "blue"); // ForceClientSkin(client, headModel, "blue"); break; } // don't ever use a default skin in teamplay, it would just waste memory // however bots will always join a team but they spawn in as spectator if ( g_gametype.integer >= GT_TEAM && team == TEAM_SPECTATOR) { ForceClientSkin(client, model, "red"); // ForceClientSkin(client, headModel, "red"); } */ #ifdef MISSIONPACK if (g_gametype.integer >= GT_TEAM) { client->pers.teamInfo = qtrue; } else { s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { client->pers.teamInfo = qtrue; } else { client->pers.teamInfo = qfalse; } } #else // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if ( ! *s || atoi( s ) != 0 ) { client->pers.teamInfo = qtrue; } else { client->pers.teamInfo = qfalse; } #endif /* s = Info_ValueForKey( userinfo, "cg_pmove_fixed" ); if ( !*s || atoi( s ) == 0 ) { client->pers.pmoveFixed = qfalse; } else { client->pers.pmoveFixed = qtrue; } */ // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi(Info_ValueForKey(userinfo, "teamtask")); // team Leader (1 = leader, 0 is normal player) teamLeader = client->sess.teamLeader; // colors strcpy(c1, Info_ValueForKey( userinfo, "color1" )); strcpy(c2, Info_ValueForKey( userinfo, "color2" )); strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds if ( ent->r.svFlags & SVF_BOT ) { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", client->pers.netname, team, model, headModel, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); } else { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\g_redteam\\%s\\g_blueteam\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\tl\\%d", client->pers.netname, client->sess.sessionTeam, model, headModel, redTeam, blueTeam, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader); } trap_SetConfigstring( CS_PLAYERS+clientNum, s ); // this is not the userinfo, more like the configstring actually G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, s ); }
/** * @brief - send name, team and value when there is a winner - else empty string * and TEAM_FREE = 0 (client structure is only used for awards!) * - connectedClients have a team but keep the check for TEAM_FREE * ... we'll never know for sure, connectedClients are determined in CalculateRanks */ void G_BuildEndgameStats(void) { char buffer[1024]; int i, j; gclient_t *best = NULL; int bestClientNum = -1; float mapXP, bestMapXP = 0.f; G_CalcClientAccuracies(); for (i = 0; i < level.numConnectedClients; i++) { level.clients[i].hasaward = qfalse; } *buffer = '\0'; // highest ranking officer - check rank, then medals and XP for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (!best || cl->sess.rank > best->sess.rank) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->sess.rank == best->sess.rank && cl->medals > best->medals) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->sess.rank == best->sess.rank && cl->medals == best->medals && cl->ps.persistant[PERS_SCORE] > best->ps.persistant[PERS_SCORE]) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i 0 %i ", bestClientNum, best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; #ifdef FEATURE_RATING // highest rated player - check skill rating for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->sess.mu - 3 * cl->sess.sigma <= 0) { continue; } if (!best || (cl->sess.mu - 3 * cl->sess.sigma) > (best->sess.mu - 3 * best->sess.sigma)) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %.2f %i ", bestClientNum, MIN(best->sess.mu - 3 * best->sess.sigma, 50.f), best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; #endif // highest experience points - check XP (total in campaign, otherwise this map only then total XP) for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->ps.persistant[PERS_SCORE] <= 0) { continue; } if (g_gametype.integer == GT_WOLF_CAMPAIGN) { if (!best || cl->ps.persistant[PERS_SCORE] > best->ps.persistant[PERS_SCORE]) { best = cl; bestClientNum = level.sortedClients[i]; } } else { mapXP = 0.f; for (j = 0; j < SK_NUM_SKILLS; j++) { mapXP += (cl->sess.skillpoints[j] - cl->sess.startskillpoints[j]); } if (!best || mapXP > bestMapXP) { best = cl; bestMapXP = mapXP; bestClientNum = level.sortedClients[i]; } else if (mapXP == bestMapXP && cl->ps.persistant[PERS_SCORE] > best->ps.persistant[PERS_SCORE]) { best = cl; bestMapXP = mapXP; bestClientNum = level.sortedClients[i]; } } } if (best) { best->hasaward = qtrue; if (g_gametype.integer == GT_WOLF_CAMPAIGN) { Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, best->ps.persistant[PERS_SCORE], best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, (int)bestMapXP, best->sess.sessionTeam)); } } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // most highly decorated - check medals then XP for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->medals <= 0) { continue; } if (!best || cl->medals > best->medals) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->medals == best->medals && cl->ps.persistant[PERS_SCORE] > best->ps.persistant[PERS_SCORE]) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, best->medals, best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } // highest fragger - check kills, then damage given for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->sess.kills <= 0) { continue; } if (!best || cl->sess.kills > best->sess.kills) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->sess.kills == best->sess.kills && cl->sess.damage_given > best->sess.damage_given) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, best->sess.kills, best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // highest skills - check skills points (this map only, min lvl 1) for (i = 0; i < SK_NUM_SKILLS; i++) { best = NULL; for (j = 0; j < level.numConnectedClients; j++) { gclient_t *cl = &level.clients[level.sortedClients[j]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if ((cl->sess.skillpoints[i] - cl->sess.startskillpoints[i]) <= 0) { continue; } if (cl->sess.skill[i] < 1) { continue; } if (!best || (cl->sess.skillpoints[i] - cl->sess.startskillpoints[i]) > (best->sess.skillpoints[i] - best->sess.startskillpoints[i])) { best = cl; bestClientNum = level.sortedClients[j]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, (int)(best->sess.skillpoints[i] - best->sess.startskillpoints[i]), best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } } best = NULL; // highest accuracy for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->acc <= 0) { continue; } if (!best || cl->acc > best->acc) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %.2f %i ", bestClientNum, best->acc < 100.f ? (double)best->acc : 100., best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // highest HS percentage for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->hspct <= 0) { continue; } if (!best || cl->hspct > best->hspct) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %.2f %i ", bestClientNum, best->hspct < 100.f ? (double)best->hspct : 100., best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // best survivor - check time played percentage (min 50% of map duration) for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if ((level.time - cl->pers.enterTime) / (float)(level.time - level.intermissiontime) < 0.5f) { continue; } if (!best || (cl->sess.time_played / (float)(level.time - cl->pers.enterTime)) > (best->sess.time_played / (float)(level.time - best->pers.enterTime))) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %.2f %i ", bestClientNum, MIN(100. * best->sess.time_played / (double)(level.time - best->pers.enterTime), 100.), best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // most gibs - check gibs, then damage given for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->sess.gibs <= 0) { continue; } if (!best || cl->sess.gibs > best->sess.gibs) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->sess.gibs == best->sess.gibs && cl->sess.damage_given > best->sess.damage_given) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, best->sess.gibs, best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // most selfkill - check selfkills, then deaths for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->sess.self_kills <= 0) { continue; } if (!best || cl->sess.self_kills > best->sess.self_kills) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->sess.self_kills == best->sess.self_kills && cl->sess.deaths > best->sess.deaths) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, best->sess.self_kills, best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // most deaths - check deaths, then damage received for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (cl->sess.deaths <= 0) { continue; } if (!best || cl->sess.deaths > best->sess.deaths) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->sess.deaths == best->sess.deaths && cl->sess.damage_received > best->sess.damage_received) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; Q_strcat(buffer, 1024, va("%i %i %i ", bestClientNum, best->sess.deaths, best->sess.sessionTeam)); } else { Q_strcat(buffer, 1024, "-1 0 0 "); } best = NULL; // I ain't got no friends award - check team kills, then team damage given (min 5 tks) for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (!best || cl->sess.team_kills > best->sess.team_kills) { best = cl; bestClientNum = level.sortedClients[i]; } else if (cl->sess.team_kills == best->sess.team_kills && cl->sess.team_damage_given > best->sess.team_damage_given) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { best->hasaward = qtrue; } Q_strcat(buffer, 1024, va("%i %i %i ", best && best->sess.team_kills >= 5 ? bestClientNum : -1, best ? best->sess.team_kills : 0, best && best->sess.team_kills >= 5 ? best->sess.sessionTeam : TEAM_FREE)); best = NULL; // welcome newbie! award - dont get this if any other award given or > 100 xp (this map) for (i = 0; i < level.numConnectedClients; i++) { gclient_t *cl = &level.clients[level.sortedClients[i]]; if (cl->sess.sessionTeam == TEAM_FREE) { continue; } if (!best || (cl->ps.persistant[PERS_SCORE] / (float)(level.time - cl->pers.enterTime)) > (best->ps.persistant[PERS_SCORE] / (float)(level.time - best->pers.enterTime))) { best = cl; bestClientNum = level.sortedClients[i]; } } if (best) { if ((best->sess.startxptotal - best->ps.persistant[PERS_SCORE]) >= 100 || best->medals || best->hasaward) { best = NULL; } } Q_strcat(buffer, 1024, va("%i %i %i ", best ? bestClientNum : -1, best ? best->ps.persistant[PERS_SCORE] : 0, best ? best->sess.sessionTeam : TEAM_FREE)); trap_SetConfigstring(CS_ENDGAME_STATS, buffer); }
void SP_trigger_objective_info(gentity_t *ent) { char *scorestring; char *customimage; int cix, cia, objflags; if (!ent->track) { G_Error("'trigger_objective_info' does not have a 'track' \n"); } if (ent->spawnflags & MESSAGE_OVERRIDE) { if (!ent->spawnitem) { G_Error("'trigger_objective_info' has override flag set but no override text\n"); } } // for specifying which commandmap objectives this entity "belongs" to G_SpawnInt("objflags", "0", &objflags); if (G_SpawnString("customimage", "", &customimage)) { cix = cia = G_ShaderIndex(customimage); } else { if (G_SpawnString("customaxisimage", "", &customimage)) { cix = G_ShaderIndex(customimage); } else { cix = 0; } if (G_SpawnString("customalliesimage", "", &customimage)) { cia = G_ShaderIndex(customimage); } else if (G_SpawnString("customalliedimage", "", &customimage)) { cia = G_ShaderIndex(customimage); } else { cia = 0; } } G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "e", va("%i", (int)(ent - g_entities))); G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "o", va("%i", objflags)); if (cix) { G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "cix", va("%i", cix)); } if (cia) { G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "cia", va("%i", cia)); } G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "s", va("%i", ent->spawnflags)); G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "n", ent->message ? ent->message : ""); if (level.numOidTriggers >= MAX_OID_TRIGGERS) { G_Error("Exceeded maximum number of 'trigger_objective_info' entities\n"); } // if this trigger has a "score" field set, then blowing up an objective // inside of this field will add "score" to the right player team. storing this // in ent->accuracy since that's unused. G_SpawnString("score", "0", &scorestring); ent->accuracy = atof(scorestring); trap_SetConfigstring(CS_OID_TRIGGERS + level.numOidTriggers, ent->track); InitTrigger(ent); if (ent->s.origin[0] || ent->s.origin[1] || ent->s.origin[2]) { G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "x", va("%i", (int)ent->s.origin[0])); G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "y", va("%i", (int)ent->s.origin[1])); G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "z", va("%i", (int)ent->s.origin[2])); } else { vec3_t mid; VectorAdd(ent->r.absmin, ent->r.absmax, mid); VectorScale(mid, 0.5f, mid); G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "x", va("%i", (int)mid[0])); G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "y", va("%i", (int)mid[1])); G_SetConfigStringValue(CS_OID_DATA + level.numOidTriggers, "z", va("%i", (int)mid[2])); } ent->s.teamNum = level.numOidTriggers++; // unlike other triggers, we need to send this one to the client ent->r.svFlags &= ~SVF_NOCLIENT; ent->s.eType = ET_OID_TRIGGER; if (!ent->target) { // no target - just link and go trap_LinkEntity(ent); } else { // finalize spawing on fourth frame to allow for proper linking with targets ent->nextthink = level.time + (3 * FRAMETIME); ent->think = Think_SetupObjectiveInfo; } }
/* ================== BotSetInfoConfigString ================== */ void BotSetInfoConfigString(bot_state_t *bs) { char goalname[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char action[MAX_MESSAGE_SIZE]; char *leader, carrying[32], *cs; bot_goal_t goal; // ClientName(bs->client, netname, sizeof(netname)); if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L"; else leader = " "; strcpy(carrying, " "); if (gametype == GT_CTF) { if (BotCTFCarryingFlag(bs)) { strcpy(carrying, "F "); } } #ifdef MISSIONPACK else if (gametype == GT_1FCTF) { if (Bot1FCTFCarryingFlag(bs)) { strcpy(carrying, "F "); } } else if (gametype == GT_HARVESTER) { if (BotHarvesterCarryingCubes(bs)) { if (BotTeam(bs) == TEAM_RED) Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_REDCUBE]); else Com_sprintf(carrying, sizeof(carrying), "%2d", bs->inventory[INVENTORY_BLUECUBE]); } } #endif switch(bs->ltgtype) { case LTG_TEAMHELP: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "helping %s", goalname); break; } case LTG_TEAMACCOMPANY: { EasyClientName(bs->teammate, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "accompanying %s", goalname); break; } case LTG_DEFENDKEYAREA: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "defending %s", goalname); break; } case LTG_GETITEM: { trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "getting item %s", goalname); break; } case LTG_KILL: { ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "killing %s", goalname); break; } case LTG_CAMP: case LTG_CAMPORDER: { Com_sprintf(action, sizeof(action), "camping"); break; } case LTG_PATROL: { Com_sprintf(action, sizeof(action), "patrolling"); break; } case LTG_GETFLAG: { Com_sprintf(action, sizeof(action), "capturing flag"); break; } case LTG_RUSHBASE: { Com_sprintf(action, sizeof(action), "rushing base"); break; } case LTG_RETURNFLAG: { Com_sprintf(action, sizeof(action), "returning flag"); break; } case LTG_ATTACKENEMYBASE: { Com_sprintf(action, sizeof(action), "attacking the enemy base"); break; } case LTG_HARVEST: { Com_sprintf(action, sizeof(action), "harvesting"); break; } default: { trap_BotGetTopGoal(bs->gs, &goal); trap_BotGoalName(goal.number, goalname, sizeof(goalname)); Com_sprintf(action, sizeof(action), "roaming %s", goalname); break; } } cs = va("l\\%s\\c\\%s\\a\\%s", leader, carrying, action); trap_SetConfigstring (CS_BOTINFO + bs->client, cs); }
/* ================ G_SetEntState sets the entstate of an entity. ================ */ void G_SetEntState(gentity_t *ent, entState_t state) { if (ent->entstate == state) { G_DPrintf("G_SetEntState: entity %i [%s] already in desired state [%i]\n", ent->s.number, ent->classname, state); return; } switch (state) { case STATE_DEFAULT: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_DEFAULT; ent->s.powerups = STATE_DEFAULT; if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 &= ~256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType != ET_COMMANDMAP_MARKER) { trap_LinkEntity(ent); } // deal with any entities in the solid { int listedEntities, e; int entityList[MAX_GENTITIES]; gentity_t *check, *block; listedEntities = trap_EntitiesInBox(ent->r.absmin, ent->r.absmax, entityList, MAX_GENTITIES); for (e = 0; e < listedEntities; e++) { check = &g_entities[entityList[e]]; // ignore everything but items, players and missiles (grenades too) if (check->s.eType != ET_MISSILE && check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject) { continue; } if ((block = G_TestEntityPosition(check)) == NULL) { continue; } if (block != ent) { // the entity is blocked by another entity - that block this should take care of this itself continue; } if (check->client || check->s.eType == ET_CORPSE) { // gibs anything player like G_Damage(check, ent, ent, NULL, NULL, 9999, DAMAGE_NO_PROTECTION, MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER); } else if (check->s.eType == ET_ITEM && check->item->giType == IT_TEAM) { // see if it's a critical entity, one that we can't just simply kill (basically flags) Team_DroppedFlagThink(check); } else { // remove the landmine from both teamlists if (check->s.eType == ET_MISSILE && check->methodOfDeath == MOD_LANDMINE) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } // just get rid of it G_FreeEntity(check); } } } break; case STATE_UNDERCONSTRUCTION: ent->entstate = STATE_UNDERCONSTRUCTION; ent->s.powerups = STATE_UNDERCONSTRUCTION; ent->realClipmask = ent->clipmask; if (ent->s.eType != ET_CONSTRUCTIBLE) // don't make nonsolid as we want to make them partially solid for staged construction { ent->clipmask = 0; } ent->realContents = ent->r.contents; if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->r.contents = 0; } if (ent->s.eFlags & EF_NONSOLID_BMODEL) { ent->realNonSolidBModel = qtrue; } else if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->s.eFlags |= EF_NONSOLID_BMODEL; } if (!Q_stricmp(ent->classname, "misc_mg42")) { // stop using the mg42 mg42_stopusing(ent); } else if (!Q_stricmp(ent->classname, "misc_aagun")) { aagun_stopusing(ent); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_LinkEntity(ent); break; case STATE_INVISIBLE: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_INVISIBLE; ent->s.powerups = STATE_INVISIBLE; if (!Q_stricmp(ent->classname, "misc_mg42")) { mg42_stopusing(ent); } else if (!Q_stricmp(ent->classname, "misc_aagun")) { aagun_stopusing(ent); } else if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 |= 256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_UnlinkEntity(ent); break; } }
/*QUAKED worldspawn (0 0 0) ? Every map should have exactly one worldspawn. "music" music wav file "gravity" 800 is default gravity "message" Text to print during connection process */ void SP_worldspawn( void ) { char *s; G_SpawnString( "classname", "", &s ); if ( Q_stricmp( s, "worldspawn" ) ) { G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" ); } // make some data visible to connecting client trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION ); trap_SetConfigstring( CS_LEVEL_START_TIME, va("%i", level.startTime ) ); G_SpawnString( "music", "", &s ); trap_SetConfigstring( CS_MUSIC, s ); G_SpawnString( "message", "", &s ); trap_SetConfigstring( CS_MESSAGE, s ); // map specific message G_SpawnString( "author", "", &s ); trap_SetConfigstring( CS_AUTHOR1, s ); // map specific author G_SpawnString( "author2", "", &s ); trap_SetConfigstring( CS_AUTHOR2, s ); // map specific author trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day if ( G_SpawnString("gravity", "", &s) ) trap_Cvar_LSet( "g_gravity", s ); else trap_Cvar_Set( "g_gravity", "800" ); //todo: revert to original gravity value G_SpawnString( "enableDust", "0", &s ); trap_Cvar_Set( "g_enableDust", s ); G_SpawnString( "enableBreath", "0", &s ); trap_Cvar_Set( "g_enableBreath", gt[g_gametype.integer].freeze ? "1" : s ); G_SpawnString( "enableSpawnPads", "0", &s ); level.enableSpawnPads = atoi( s ); //fnq3: ra3 G_SpawnString( "arena", "0", &s ); if ( s[0] ) { level.arenas_total = atoi( s ); if ( level.arenas_total > MAX_MAP_ARENAS ) level.arenas_total = MAX_MAP_ARENAS; level.arenas_active = 0; trap_SetConfigstring( CS_ARENA, va("%i", level.arenas_active) ); } //-fnq3 g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD; g_entities[ENTITYNUM_WORLD].r.ownerNum = ENTITYNUM_NONE; g_entities[ENTITYNUM_WORLD].classname = "worldspawn"; g_entities[ENTITYNUM_NONE].s.number = ENTITYNUM_NONE; g_entities[ENTITYNUM_NONE].r.ownerNum = ENTITYNUM_NONE; g_entities[ENTITYNUM_NONE].classname = "nothing"; // see if we want a warmup time trap_SetConfigstring( CS_WARMUP, "" ); if ( g_restarted.integer ) { trap_Cvar_Set( "g_restarted", "0" ); level.warmupTime = 0; level.warmupState = WARMUP_ENDED; trap_SetConfigstring( CS_WARMUP_STATE, va("%i", level.warmupState) ); if ( gt[g_gametype.integer].rounds ) { Round_Queue( FRAMETIME, qtrue ); } } else if ( g_doWarmup.integer ) { // Turn it on level.warmupTime = -1; level.warmupState = WARMUP_DEFAULT; trap_SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) ); trap_SetConfigstring( CS_WARMUP_STATE, va("%i", level.warmupState) ); G_LogPrintf( "Warmup:\n" ); } }
/* =========== 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 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; 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; }
/* =========== 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. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int health; 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 c1[ MAX_INFO_STRING ]; char c2[ MAX_INFO_STRING ]; 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) ) strcpy( userinfo, "\\name\\badinfo" ); // 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 ) ) { // in case we need to revert and there's no oldname if( client->pers.connected != CON_CONNECTED ) Q_strncpyz( oldname, "UnnamedPlayer", sizeof( oldname ) ); if( client->pers.nameChangeTime && ( level.time - client->pers.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( g_maxNameChanges.integer > 0 && client->pers.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( !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, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { if( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname ) ); G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s\" -> \"%s\"\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname ); G_admin_namelog_update( client, qfalse ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) client->pers.maxHealth = 100; 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; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( !*s || atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; // colors strcpy( c1, Info_ValueForKey( userinfo, "color1" ) ); strcpy( c2, Info_ValueForKey( userinfo, "color2" ) ); 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\\c1\\%s\\c2\\%s\\" "hc\\%i\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.teamSelection, model, c1, c2, client->pers.maxHealth, BG_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ }
void G_delayPrint(gentity_t *dpent) { int think_next = 0; qboolean fFree = qtrue; switch (dpent->spawnflags) { case DP_PAUSEINFO: { if (level.match_pause > PAUSE_UNPAUSING) { int cSeconds = match_timeoutlength.integer * 1000 - (level.time - dpent->timestamp); if (cSeconds > 1000) { AP(va("cp \"^3Match resuming in ^1%d^3 seconds!\n\"", cSeconds / 1000)); think_next = level.time + 15000; fFree = qfalse; } else { level.match_pause = PAUSE_UNPAUSING; AP("print \"^3Match resuming in 10 seconds!\n\""); G_globalSound("sound/osp/prepare.wav"); G_spawnPrintf(DP_UNPAUSING, level.time + 10, NULL); } } break; } case DP_UNPAUSING: { if (level.match_pause == PAUSE_UNPAUSING) { int cSeconds = 11 * 1000 - (level.time - dpent->timestamp); if (cSeconds > 1000) { AP(va("cp \"^3Match resuming in ^1%d^3 seconds!\n\"", cSeconds / 1000)); think_next = level.time + 1000; fFree = qfalse; } else { level.match_pause = PAUSE_NONE; G_globalSound("sound/osp/fight.wav"); G_printFull("^1FIGHT!", NULL); trap_SetConfigstring(CS_LEVEL_START_TIME, va("%i", level.startTime + level.timeDelta)); level.server_settings &= ~CV_SVS_PAUSE; trap_SetConfigstring(CS_SERVERTOGGLES, va("%d", level.server_settings)); } } break; } #ifdef FEATURE_MULTIVIEW case DP_MVSPAWN: { int i; gentity_t *ent; for (i = 0; i < level.numConnectedClients; i++) { ent = g_entities + level.sortedClients[i]; if (ent->client->pers.mvReferenceList == 0) { continue; } if (ent->client->sess.sessionTeam != TEAM_SPECTATOR) { continue; } G_smvRegenerateClients(ent, ent->client->pers.mvReferenceList); } break; } #endif default: break; } dpent->nextthink = think_next; if (fFree) { dpent->think = 0; G_FreeEntity(dpent); } }
void JMSaberTouch(gentity_t *self, gentity_t *other, trace_t *trace) { int i = 0; // gentity_t *te; if (!other || !other->client || other->health < 1) { return; } if (self->enemy) { return; } if (!self->s.modelindex) { return; } if (other->client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER)) { return; } if (other->client->ps.isJediMaster) { return; } self->enemy = other; other->client->ps.stats[STAT_WEAPONS] = (1 << WP_SABER); other->client->ps.weapon = WP_SABER; other->s.weapon = WP_SABER; G_AddEvent(other, EV_BECOME_JEDIMASTER, 0); // Track the jedi master trap_SetConfigstring ( CS_CLIENT_JEDIMASTER, va("%i", other->s.number ) ); if (g_spawnInvulnerability.integer) { other->client->ps.eFlags |= EF_INVULNERABLE; other->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer; } trap_SendServerCommand( -1, va("cp \"%s %s\n\"", other->client->pers.netname, G_GetStripEdString("SVINGAME", "BECOMEJM")) ); other->client->ps.isJediMaster = qtrue; other->client->ps.saberIndex = self->s.number; if (other->health < 200 && other->health > 0) { //full health when you become the Jedi Master other->client->ps.stats[STAT_HEALTH] = other->health = 200; } if (other->client->ps.fd.forcePower < 100) { other->client->ps.fd.forcePower = 100; } while (i < NUM_FORCE_POWERS) { other->client->ps.fd.forcePowersKnown |= (1 << i); other->client->ps.fd.forcePowerLevel[i] = FORCE_LEVEL_3; i++; } self->pos2[0] = 1; self->pos2[1] = level.time + JMSABER_RESPAWN_TIME; self->s.modelindex = 0; self->s.eFlags |= EF_NODRAW; self->s.modelGhoul2 = 0; self->s.eType = ET_GENERAL; /* te = G_TempEntity( vec3_origin, EV_DESTROY_GHOUL2_INSTANCE ); te->r.svFlags |= SVF_BROADCAST; te->s.eventParm = self->s.number; */ G_KillG2Queue(self->s.number); return; }