int32_t C4TeamList::GetForcedTeamSelection(int32_t idForPlayer) const { // if there's only one team for the player to join, return that team ID C4Team *pOKTeam = NULL, *pCheck; if (idForPlayer) pOKTeam = GetTeamByPlayerID(idForPlayer); // curent team is always possible, even if full int32_t iCheckTeam=0; while ((pCheck = GetTeamByIndex(iCheckTeam++))) if (!pCheck->IsFull()) { // this team could be joined if (pOKTeam && pOKTeam != pCheck) { // there already was a team that could be joined // two alternatives -> team selection is not forced return 0; } pOKTeam = pCheck; } // was there a team that could be joined? if (pOKTeam) { // if teams are generated on the fly, there would always be the possibility of creating a new team } if (IsAutoGenerateTeams()) return 0; // otherwise, this team is forced! return pOKTeam->GetID(); } // no team could be joined: Teams auto generated? if (IsAutoGenerateTeams()) { // then the only possible way is to join a new team return TEAMID_New; } // otherwise, nothing can be done... return 0; }
int32_t C4TeamList::GetStartupTeamCount(int32_t startup_player_count) { // Count non-empty teams int32_t i_team = 0; C4Team *team; int32_t team_count = 0; while ((team = GetTeamByIndex(i_team++))) { if (team->GetPlayerCount() > 0) ++team_count; } // No populated teams found? This can happen in non-network mode when no players are assigned if (!team_count) { // Teams have not been selected yet, but the map script may want to have an estimate // in this case, calculate prospective teams from startup player count if (IsCustom() && !IsAutoGenerateTeams()) { // Teams are pre-defined. Assume players will try to distribute broadly on these teams team_count = std::min<int32_t>(startup_player_count, GetTeamCount()); } else if (IsRandomTeam()) { // Randomized teams: Players will be put into two teams. team_count = std::min<int32_t>(startup_player_count, 2); } else { // Teams are auto-added -> fallback to player count team_count = startup_player_count; } } return team_count; }
bool C4TeamList::IsJoin2TeamAllowed(int32_t idTeam) { // join to new team: Only if new teams can be created if (idTeam == TEAMID_New) return IsAutoGenerateTeams(); // team number must be valid C4Team *pTeam = GetTeamByID(idTeam); if (!pTeam) return false; // team player count must not exceed the limit return !pTeam->IsFull(); }
bool C4TeamList::IsJoin2TeamAllowed(int32_t idTeam, C4PlayerType plrType) { // join to new team: Only if new teams can be created if (idTeam == TEAMID_New) return IsAutoGenerateTeams(); // team number must be valid C4Team *pTeam = GetTeamByID(idTeam); if (!pTeam) return false; // team player count must not exceed the limit, unless it is a script player return !pTeam->IsFull() || plrType == C4PT_Script; }
void C4TeamList::FillTeamDistOptions(C4GUI::ComboBox_FillCB *pFiller) const { // no teams if disabled if (!fActive) return; // team distribution options pFiller->AddEntry(GetTeamDistName(TEAMDIST_Free).getData(), TEAMDIST_Free); pFiller->AddEntry(GetTeamDistName(TEAMDIST_Host).getData(), TEAMDIST_Host); if (IsAutoGenerateTeams()) pFiller->AddEntry(GetTeamDistName(TEAMDIST_None).getData(), TEAMDIST_None); // no teams: only for regular melees pFiller->AddEntry(GetTeamDistName(TEAMDIST_Random).getData(), TEAMDIST_Random); pFiller->AddEntry(GetTeamDistName(TEAMDIST_RandomInv).getData(), TEAMDIST_RandomInv); }
void C4TeamList::EnsureTeamCount() { // in random autogenerate mode, there must be exactly two teams if (IsRandomTeam()) { if (IsAutoGenerateTeams() && GetTeamCount() != 2) { ClearTeams(); GenerateDefaultTeams(2); } } }
void C4TeamList::RecheckTeams() { // automatic team distributions only if (!IsRandomTeam()) return; // host decides random teams if (!::Control.isCtrlHost()) { // Still make sure that we have the right number of teams on the clients as well so that // GetTeamCount() does not report inconsistent values. EnsureTeamCount(); return; } // random teams in auto generate mode? Make sure there are exactly two teams if (IsAutoGenerateTeams() && GetTeamCount() != 2) { ReassignAllTeams(); return; } // redistribute players of largest team that has relocatable players left towards smaller teams for (;;) { C4Team *pLowestTeam = GetRandomSmallestTeam(); if (!pLowestTeam) break; // no teams: Nothing to re-distribute. // get largest team that has relocateable players C4Team *pLargestTeam = nullptr; C4Team **ppCheck=ppList; int32_t iCnt=iTeamCount; for (; iCnt--; ++ppCheck) if (!pLargestTeam || pLargestTeam->GetPlayerCount() > (*ppCheck)->GetPlayerCount()) if ((*ppCheck)->GetFirstUnjoinedPlayerID()) pLargestTeam = *ppCheck; // no team can redistribute? if (!pLargestTeam) break; // redistribution won't help much? if (pLargestTeam->GetPlayerCount() - pLowestTeam->GetPlayerCount() <= 1) break; // okay; redistribute one player! int32_t idRedistPlayer = pLargestTeam->GetFirstUnjoinedPlayerID(); C4PlayerInfo *pInfo = Game.PlayerInfos.GetPlayerInfoByID(idRedistPlayer); assert(pInfo); if (!pInfo) break; // umn...serious problems pLargestTeam->RemovePlayerByID(idRedistPlayer); pLowestTeam->AddPlayer(*pInfo, true); C4ClientPlayerInfos *pClrInfo = Game.PlayerInfos.GetClientInfoByPlayerID(idRedistPlayer); assert(pClrInfo); // player info change: mark updated to remote clients get information if (pClrInfo) { pClrInfo->SetUpdated(); } } }
bool C4TeamList::CanLocalChooseTeam(int32_t idPlayer) const { // must be possible at all if (!CanLocalChooseTeam()) return false; // there must be space in a target team // always possible if teams are generated on the fly if (IsAutoGenerateTeams()) return true; // also possible if one of the teams that's not the player's is not full C4Team *pCurrentTeam = NULL, *pCheck; if (idPlayer) pCurrentTeam = GetTeamByPlayerID(idPlayer); int32_t iCheckTeam=0; while ((pCheck = GetTeamByIndex(iCheckTeam++))) if (pCheck != pCurrentTeam) if (!pCheck->IsFull()) break; return !!pCheck; }
void C4TeamList::ReassignAllTeams() { assert(::Control.isCtrlHost()); if (!::Control.isCtrlHost()) return; // go through all player infos; reset team in them int32_t idStart = -1; C4PlayerInfo *pNfo; while ((pNfo = Game.PlayerInfos.GetNextPlayerInfoByID(idStart))) { idStart = pNfo->GetID(); if (pNfo->HasJoinIssued()) continue; pNfo->SetTeam(0); // mark changed info as updated C4ClientPlayerInfos *pCltInfo = Game.PlayerInfos.GetClientInfoByPlayerID(idStart); assert(pCltInfo); if (pCltInfo) { pCltInfo->SetUpdated(); } } // clear players from team lists RecheckPlayers(); // in random autogenerate mode, there must be exactly two teams if (IsRandomTeam()) if (IsAutoGenerateTeams() && GetTeamCount() != 2) { ClearTeams(); GenerateDefaultTeams(2); } // reassign them idStart = -1; while ((pNfo = Game.PlayerInfos.GetNextPlayerInfoByID(idStart))) { idStart = pNfo->GetID(); if (pNfo->HasJoinIssued()) continue; assert(!pNfo->GetTeam()); RecheckPlayerInfoTeams(*pNfo, true); } }
bool C4TeamList::RecheckPlayerInfoTeams(C4PlayerInfo &rNewJoin, bool fByHost) { // only if enabled assert(IsMultiTeams()); if (!IsMultiTeams()) return false; // check whether a new team is to be assigned first C4Team *pCurrentTeam = GetTeamByPlayerID(rNewJoin.GetID()); int32_t idCurrentTeam = pCurrentTeam ? pCurrentTeam->GetID() : 0; if (rNewJoin.GetTeam()) { // was that team a change to the current team? // no change anyway: OK, skip this info if (idCurrentTeam == rNewJoin.GetTeam()) return true; // the player had a different team assigned: Check if changes are allowed at all if (eTeamDist == TEAMDIST_Free || (eTeamDist == TEAMDIST_Host && fByHost)) // also make sure that selecting this team is allowed, e.g. doesn't break the team limit // this also checks whether the team number is a valid team - but it would accept TEAMID_New, which shouldn't be used in player infos! if (rNewJoin.GetTeam() != TEAMID_New && IsJoin2TeamAllowed(rNewJoin.GetTeam())) // okay; accept change return true; // Reject change by reassigning the current team rNewJoin.SetTeam(idCurrentTeam); // and determine a new team, if none has been assigned yet if (idCurrentTeam) return true; } // new team assignment // teams are always needed in the lobby, so there's a team preset to change // for runtime joins, teams are needed if specified by teams.txt or if any teams have been created before (to avoid mixed team-noteam-scenarios) // but only assign teams in runtime join if the player won't pick it himself bool fWillHaveLobby = ::Network.isEnabled() && !::Network.Status.isPastLobby() && Game.fLobby; bool fHasOrWillHaveLobby = ::Network.isLobbyActive() || fWillHaveLobby; bool fCanPickTeamAtRuntime = !IsRandomTeam() && (rNewJoin.GetType() == C4PT_User) && IsRuntimeJoinTeamChoice(); bool fIsTeamNeeded = IsRuntimeJoinTeamChoice() || GetTeamCount(); if (!fHasOrWillHaveLobby && (!fIsTeamNeeded || fCanPickTeamAtRuntime)) return false; // get least-used team C4Team *pAssignTeam=NULL; C4Team *pLowestTeam = GetRandomSmallestTeam(); // melee mode if (IsAutoGenerateTeams() && !IsRandomTeam()) { // reuse old team only if it's empty if (pLowestTeam && !pLowestTeam->GetPlayerCount()) pAssignTeam = pLowestTeam; else { // no empty team: generate new GenerateDefaultTeams(iLastTeamID+1); pAssignTeam = GetTeamByID(iLastTeamID); } } else { if (!pLowestTeam) { // not enough teams defined in teamwork mode? // then create two teams as default if (!GetTeamByIndex(1)) GenerateDefaultTeams(2); else // otherwise, all defined teams are full. This is a scenario error, because MaxPlayer should have been adjusted return false; pLowestTeam = GetTeamByIndex(0); } pAssignTeam = pLowestTeam; } // assign it if (!pAssignTeam) return false; pAssignTeam->AddPlayer(rNewJoin, true); return true; }