/* ============== JoinedTeam ============== A player just joined a team, so do things. */ void JoinedTeam (edict_t *ent, qboolean reconnected, qboolean notify) { PMenu_Close (ent); if (notify) { if (g_gamemode->value != GAMEMODE_1V1) gi.bprintf (PRINT_HIGH, "%s %sjoined team '%s'\n", ent->client->pers.netname, reconnected ? "re" : "", teaminfo[ent->client->pers.team].name); else gi.bprintf (PRINT_HIGH, "%s %sjoined the game.\n", ent->client->pers.netname, reconnected ? "re" : ""); } ent->client->resp.ready = false; //joining a team with no captain by default assigns. //FIXME: should this still assign even if the team has existing players? if (!teaminfo[ent->client->pers.team].captain) TDM_SetCaptain (ent->client->pers.team, ent); //nasty hack for setting team names for 1v1 mode TDM_UpdateTeamNames (); //if we were invited mid-game, reallocate and insert into teamplayers // wision: do not add if a player reconnected and used his joincode // skuller: force add if player was picked/invited to the different team if (tdm_match_status != MM_WARMUP && !reconnected && (!ent->client->resp.teamplayerinfo || ent->client->resp.teamplayerinfo->team != ent->client->pers.team)) TDM_AddPlayerToMatchinfo (ent); //wision: set skin for new player gi.configstring (CS_PLAYERSKINS + (ent - g_edicts) - 1, va("%s\\%s", ent->client->pers.netname, teaminfo[ent->client->pers.team].skin)); //set everyone elses teamskin for this player based on what team he is on TDM_SetAllTeamSkins (ent); //set this players teamskins based on his team TDM_SetTeamSkins (ent, NULL); if (g_gamemode->value != GAMEMODE_1V1) gi.configstring (CS_TDM_SPECTATOR_STRINGS + (ent - g_edicts) - 1, va ("%s (%s)", ent->client->pers.netname, teaminfo[ent->client->pers.team].name)); else gi.configstring (CS_TDM_SPECTATOR_STRINGS + (ent - g_edicts) - 1, ent->client->pers.netname); TDM_TeamsChanged (); respawn (ent); // switch from spec to player statusbar, if weapon hud is enabled, that'll happen in respawn() if (!UF(ent, WEAPON_HUD)) { TDM_UpdateHud(ent, true); } }
/* ============== ToggleChaseCam ============== Player hit Spectator menu option or used chase command. */ void ToggleChaseCam (edict_t *ent) { if (ent->client->pers.team) { TDM_LeftTeam (ent, true); TDM_TeamsChanged (); if (tdm_match_status == MM_TIMEOUT && teaminfo[TEAM_A].players == 0 && teaminfo[TEAM_B].players == 0) TDM_ResumeGame (); respawn (ent); } if (ent->client->chase_target) DisableChaseCam (ent); else GetChaseTarget(ent); PMenu_Close (ent); }
/* ============== TDM_SetupClient ============== Setup the client after an initial connection. Called on first spawn only, not every map. Returns true if a join code was used to respawn a full player, so that PutClientInServer knows about it. */ qboolean TDM_SetupClient (edict_t *ent) { ent->client->pers.team = TEAM_SPEC; TDM_TeamsChanged (); //handled in userinfo updates now //TDM_DownloadPlayerConfig (ent); if (!TDM_ProcessJoinCode (ent, 0)) { if (tdm_match_status == MM_TIMEOUT) { gi.cprintf (ent, PRINT_CHAT, "\nMatch is currently paused and will auto resume in %s.\n", TDM_SecsToString(FRAMES_TO_SECS(level.timeout_end_framenum - level.realframenum))); } else { TDM_ShowTeamMenu (ent); gi.cprintf (ent, PRINT_CHAT, "\nWelcome to OpenTDM!\nType 'commands' in the console for a brief command guide.\n\n"); } } else { if (tdm_match_status == MM_TIMEOUT) { if (TDM_Is1V1()) { //only resume if we have 2 players - it's possible both players dropped, and that is a situation //we need to be able to handle. if (teaminfo[TEAM_A].players == 1 && teaminfo[TEAM_B].players == 1) TDM_ResumeGame (); return true; } } } return false; }
/* ============== TDM_Disconnected ============== A player disconnected, do things. */ void TDM_Disconnected (edict_t *ent) { qboolean removeTimeout; removeTimeout = false; //have to check this right up here since we nuke the teamplayer just below! if (tdm_match_status == MM_TIMEOUT && level.tdm_timeout_caller && level.tdm_timeout_caller->client == ent) removeTimeout = true; //we remove this up here so TDM_LeftTeam doesn't try to resume if we become implicit timeout caller TDM_RemoveStatsLink (ent); if (ent->client->pers.team) { if (tdm_match_status >= MM_PLAYING && tdm_match_status != MM_SCOREBOARD) { //do joincode stuff if a team player disconnects - save all their client info ent->client->resp.teamplayerinfo->saved_client = gi.TagMalloc (sizeof(gclient_t), TAG_GAME); *ent->client->resp.teamplayerinfo->saved_client = *ent->client; ent->client->resp.teamplayerinfo->saved_entity = gi.TagMalloc (sizeof(edict_t), TAG_GAME); *ent->client->resp.teamplayerinfo->saved_entity = *ent; if (TDM_Is1V1() && g_1v1_timeout->value > 0) { edict_t *ghost; //may have already been set by a player or previous client disconnect if (tdm_match_status != MM_TIMEOUT) { edict_t *opponent; //timeout is called implicitly in 1v1 games or the other player would auto win level.timeout_end_framenum = level.realframenum + SECS_TO_FRAMES(g_1v1_timeout->value); level.last_tdm_match_status = tdm_match_status; tdm_match_status = MM_TIMEOUT; level.tdm_timeout_caller = ent->client->resp.teamplayerinfo; gi.bprintf (PRINT_CHAT, "%s disconnected and has %s to reconnect.\n", level.tdm_timeout_caller->name, TDM_SecsToString (g_1v1_timeout->value)); //show the opponent their options opponent = TDM_FindPlayerForTeam (TEAM_A); if (opponent) gi.cprintf (opponent, PRINT_HIGH, "Your opponent has disconnected. You can allow them %s to reconnect, or you can force a forfeit by typing 'win' in the console.\n", TDM_SecsToString (g_1v1_timeout->value)); opponent = TDM_FindPlayerForTeam (TEAM_B); if (opponent) gi.cprintf (opponent, PRINT_HIGH, "Your opponent has disconnected. You can allow them %s to reconnect, or you can force a forfeit by typing 'win' in the console.\n", TDM_SecsToString (g_1v1_timeout->value)); } //show a "ghost" player where the player was ghost = G_Spawn (); VectorCopy (ent->s.origin, ghost->s.origin); VectorCopy (ent->s.origin, ghost->old_origin); VectorCopy (ent->s.angles, ghost->s.angles); ghost->s.effects = EF_SPHERETRANS; ghost->s.modelindex = 255; ghost->s.modelindex2 = 255; ghost->s.skinnum = ent - g_edicts - 1; ghost->s.frame = ent->s.frame; ghost->count = ent->s.number; ghost->classname = "ghost"; ghost->target_ent = ent; ghost->enttype = ENT_GHOST; gi.linkentity (ghost); } } if (removeTimeout) TDM_ResumeGame (); TDM_LeftTeam (ent, false); } TDM_TeamsChanged (); if (tdm_match_status == MM_WARMUP && teaminfo[TEAM_SPEC].players == 0 && teaminfo[TEAM_A].players == 0 && teaminfo[TEAM_B].players == 0) { if (vote.active) TDM_RemoveVote (); //reset the map if it's been running for over 7 days to workaround time precision bugs in the engine, fixes 0000208 if (time (NULL) - level.spawntime > (86400 * 7)) { char command[256]; Com_sprintf (command, sizeof(command), "gamemap \"%s\"\n", level.mapname); gi.AddCommandString (command); } } else TDM_CheckVote (); //zero for connecting clients on server browsers ent->client->ps.stats[STAT_FRAGS] = 0; }