示例#1
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;
}
示例#2
0
void C4GameSave::WriteDescPlayers(StdStrBuf &sBuf)
{
	// New style using Game.PlayerInfos
	if (Game.PlayerInfos.GetPlayerCount())
	{
		sBuf.Append(LoadResStr("IDS_DESC_PLRS"));
		if (Game.Teams.IsMultiTeams() && !Game.Teams.IsAutoGenerateTeams())
		{
			// Teams defined: Print players sorted by teams
			WriteDescLineFeed(sBuf);
			C4Team *pTeam; int32_t i=0;
			while ((pTeam = Game.Teams.GetTeamByIndex(i++)))
			{
				WriteDescPlayers(sBuf, true, pTeam->GetID());
			}
			// Finally, print out players outside known teams (those can only be achieved by script using SetPlayerTeam)
			WriteDescPlayers(sBuf, true, 0);
		}
		else
		{
			// No teams defined: Print all players that have ever joined
			WriteDescPlayers(sBuf, false, 0);
		}
	}
}
示例#3
0
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;
}
示例#4
0
void C4GameSave::WriteDescPlayers(StdStrBuf &sBuf, bool fByTeam, int32_t idTeam)
{
	// write out all players; only if they match the given team if specified
	C4PlayerInfo *pPlr; bool fAnyPlrWritten = false;
	for (int i = 0; (pPlr = Game.PlayerInfos.GetPlayerInfoByIndex(i)); i++)
		if (pPlr->HasJoined() && !pPlr->IsRemoved() && !pPlr->IsInvisible())
		{
			if (fByTeam)
			{
				if (idTeam)
				{
					// match team
					if (pPlr->GetTeam() != idTeam) continue;
				}
				else
				{
					// must be in no known team
					if (Game.Teams.GetTeamByID(pPlr->GetTeam())) continue;
				}
			}
			if (fAnyPlrWritten)
				sBuf.Append(", ");
			else if (fByTeam && idTeam)
			{
				C4Team *pTeam = Game.Teams.GetTeamByID(idTeam);
				if (pTeam) sBuf.AppendFormat("%s: ", pTeam->GetName());
			}
			sBuf.Append(pPlr->GetName());
			fAnyPlrWritten = true;
		}
	if (fAnyPlrWritten) WriteDescLineFeed(sBuf);
}
示例#5
0
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();
}
示例#6
0
C4Team *C4TeamList::CreateTeam(const char *szName)
{
	// custom team
	C4Team *pNewTeam = new C4Team();
	pNewTeam->iID = iLastTeamID + 1;
	SCopy(szName, pNewTeam->Name, C4MaxName);
	AddTeam(pNewTeam);
	pNewTeam->RecheckColor(*this);
	return pNewTeam;
}
示例#7
0
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;
}
示例#8
0
C4Team *C4TeamList::GetRandomSmallestTeam() const
{
	C4Team *pLowestTeam = NULL; int iLowestTeamCount = 0;
	C4Team **ppCheck=ppList; int32_t iCnt=iTeamCount;
	for (; iCnt--; ++ppCheck)
	{
		if ((*ppCheck)->IsFull()) continue; // do not join into full teams
		if (!pLowestTeam || pLowestTeam->GetPlayerCount() > (*ppCheck)->GetPlayerCount())
		{
			pLowestTeam = *ppCheck;
			iLowestTeamCount = 1;
		}
		else if (pLowestTeam->GetPlayerCount() == (*ppCheck)->GetPlayerCount())
			if (!SafeRandom(++iLowestTeamCount))
				pLowestTeam = *ppCheck;
	}
	return pLowestTeam;
}
示例#9
0
bool C4TeamList::Load(C4Group &hGroup, class C4Scenario *pInitDefault, class C4LangStringTable *pLang)
{
	// clear previous
	Clear();
	// load file contents
	StdStrBuf Buf;
	if (!hGroup.LoadEntryString(C4CFN_Teams, &Buf))
	{
		// no teams: Try default init
		if (!pInitDefault) return false;
		// no teams defined: Activate default melee teams if a melee rule is found
		// default: FFA for anything that looks like melee
		if ( pInitDefault->Game.Goals.GetIDCount(C4ID::Melee, 1))
		{
			fAllowHostilityChange = true;
			fActive = true;
			fAutoGenerateTeams = true;
		}
		else
		{
			// No goals/rules whatsoever: They could be present in the objects.txt, but parsing that would be a bit of
			//  overkill
			// So just keep the old behaviour here, and disallow teams
			fAllowHostilityChange = true;
			fActive = false;
		}
		fCustom = false;
	}
	else
	{
		// team definition file may be localized
		if (pLang) pLang->ReplaceStrings(Buf);
		// compile
		if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "Teams"), Buf, C4CFN_Teams)) return false;
	}
	// post-initialization: Generate default team colors
	int32_t iTeam=0; C4Team *pTeam;
	while ((pTeam = GetTeamByIndex(iTeam++)))
		pTeam->RecheckColor(*this);
	return true;
}
示例#10
0
void C4Team::RecheckColor(C4TeamList &rForList)
{
	// number of times trying new player colors
	const int32_t C4MaxTeamColorChangeTries = 100;
	if (!dwClr)
	{
		const int defTeamColorCount = 10;
		DWORD defTeamColorRGB[defTeamColorCount] = { 0xF40000, 0x00C800, 0xFCF41C, 0x2020FF, // red, green, yellow, blue,
		    0xC48444, 0xFFFFFF, 0x848484, 0xFF00EF, // brown, white, grey, pink,
		    0x00FFFF, 0x784830
		                                           }; // cyan, dk brown
		// no color assigned yet: Generate by team ID
		if (iID >=1 && iID <=defTeamColorCount)
		{
			// default colors
			dwClr = defTeamColorRGB[iID-1] | 0xff000000;
		}
		else
		{
			// find a new, unused color
			for (int32_t iTry=1; iTry<C4MaxTeamColorChangeTries; ++iTry)
			{
				dwClr = GenerateRandomPlayerColor(iTry);
				int32_t iIdx=0; C4Team *pTeam; bool fOK=true;
				while ((pTeam = rForList.GetTeamByIndex(iIdx++)))
					if (pTeam != this)
						if (IsColorConflict(pTeam->GetColor(), dwClr))
						{
							fOK=false;
							break;
						}
				// color is fine?
				if (fOK) return;
				// it's not; try next color
			}
			// Giving up: Use last generated color
		}
	}
}
示例#11
0
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();
		}
	}
}
示例#12
0
C4Team::C4Team(const C4Team &rCopy)
		: piPlayers(new int32_t[rCopy.GetPlayerCount()]),
		iPlayerCount(rCopy.GetPlayerCount()),
		iPlayerCapacity(rCopy.GetPlayerCount()),
		iID(rCopy.GetID()), iPlrStartIndex(rCopy.iPlrStartIndex), dwClr(rCopy.dwClr),
		sIconSpec(rCopy.GetIconSpec()), iMaxPlayer(rCopy.iMaxPlayer)
{
	// copy name
	SCopy(rCopy.GetName(), Name, C4MaxName);
	// copy players
	for (int32_t i = 0; i < iPlayerCount; i++)
		piPlayers[i] = rCopy.GetIndexedPlayer(i);
}
示例#13
0
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;
}
示例#14
0
bool C4MainMenu::DoRefillInternal(bool &rfRefilled)
{
	// Variables
	C4FacetSurface fctSymbol;
	C4Player *pPlayer;
	C4IDList ListItems;
	C4Facet fctTarget;
	bool fWasEmpty = !GetItemCount();

	// Refill
	switch (Identification)
	{
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	case C4MN_Hostility:
	{
		// Clear items
		ClearItems();
		// Refill player
		if (!(pPlayer = ::Players.Get(Player))) return false;
		// Refill items
		C4Player *pPlr; int32_t iIndex;
		for (iIndex=0; (pPlr = ::Players.GetByIndex(iIndex)); iIndex++)
			// Ignore player self and invisible
			if (pPlr != pPlayer) if (!pPlr->IsInvisible())
				{
					// Symbol
					fctSymbol.Create(C4SymbolSize,C4SymbolSize);
					pPlayer->DrawHostility(fctSymbol,iIndex);
					// Message
					StdStrBuf sMsg;
					bool isFriendly = pPlayer->Hostility.find(pPlr) == pPlayer->Hostility.end();
					if (isFriendly)
						sMsg.Format(LoadResStr("IDS_MENU_ATTACK"),pPlr->GetName());
					else
						sMsg.Format(LoadResStr("IDS_MENU_NOATTACK"),pPlr->GetName());
					// Command
					char szCommand[1000];
					sprintf(szCommand,"SetHostility:%i",pPlr->Number);
					// Info caption
					char szInfoCaption[C4MaxTitle+1],szFriendly[50],szNot[30]="";
					SCopy(LoadResStr(isFriendly ? "IDS_MENU_ATTACKHOSTILE" : "IDS_MENU_ATTACKFRIENDLY"),szFriendly);
					if (!isFriendly) SCopy(LoadResStr("IDS_MENU_ATTACKNOT"),szNot);
					sprintf(szInfoCaption,LoadResStr("IDS_MENU_ATTACKINFO"),pPlr->GetName(),szFriendly,szNot);
					if (iIndex==pPlayer->Number) SCopy(LoadResStr("IDS_MENU_ATTACKSELF"),szInfoCaption);
					// Add item
					Add(sMsg.getData(),fctSymbol,szCommand,C4MN_Item_NoCount,NULL,szInfoCaption);
					fctSymbol.Default();
				}
		break;
	}
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	case C4MN_TeamSelection:
	case C4MN_TeamSwitch:
	{
		// Clear items
		ClearItems();
		// add all teams as menu items
		// 2do: Icon
		C4Team *pTeam; int32_t i=0; bool fAddNewTeam=Game.Teams.IsAutoGenerateTeams();
		for (;;)
		{
			pTeam = Game.Teams.GetTeamByIndex(i);
			if (pTeam)
			{
				// next regular team
				++i;
				// do not add a new team if an empty team exists
				if (!pTeam->GetPlayerCount()) fAddNewTeam = false;
			}
			else if (fAddNewTeam)
			{
				// join new team
				fAddNewTeam = false;
			}
			else
			{
				// all teams done
				break;
			}
			// create team symbol: Icon spec if specified; otherwise flag for empty and crew for nonempty team
			fctSymbol.Create(C4SymbolSize,C4SymbolSize);
			const char *szIconSpec = pTeam ? pTeam->GetIconSpec() : NULL;
			bool fHasIcon = false;
			if (szIconSpec && *szIconSpec)
			{
				fHasIcon = Game.DrawTextSpecImage(fctSymbol, szIconSpec, NULL, pTeam->GetColor());
			}
			if (!fHasIcon)
			{
				if (pTeam && pTeam->GetPlayerCount())
					::GraphicsResource.fctCrewClr.DrawClr(fctSymbol, true, pTeam->GetColor());
				else
					C4GUI::Icon::GetIconFacet(C4GUI::Ico_Team).Draw(fctSymbol, true);
			}
			StdStrBuf sTeamName;
			if (pTeam)
			{
				sTeamName.Take(pTeam->GetNameWithParticipants());
			}
			else
				sTeamName.Ref(LoadResStr("IDS_PRC_NEWTEAM"));
			const char *szOperation = (Identification == C4MN_TeamSwitch) ? "TeamSwitch" : "TeamSel";
			Add(sTeamName.getData(), fctSymbol,FormatString("%s:%d", szOperation, pTeam ? pTeam->GetID() : TEAMID_New).getData(),
			    C4MN_Item_NoCount,NULL,FormatString(LoadResStr("IDS_MSG_JOINTEAM"), sTeamName.getData()).getData(), C4ID(pTeam ? pTeam->GetID() : 0));
			fctSymbol.Default();
		}
		break;
	}
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	case C4MN_Observer: // observer menu
	{
		// Clear items
		ClearItems();
		// Check validity
		C4Viewport *pVP = ::Viewports.GetViewport(NO_OWNER);
		if (!pVP) return false;
		int32_t iInitialSelection = 0;
		// Add free view
		AddRefSym(LoadResStr("IDS_MSG_FREEVIEW"), C4GUI::Icon::GetIconFacet(C4GUI::Ico_Star), "Observe:Free", C4MN_Item_NoCount, NULL, LoadResStr("IDS_MSG_FREELYSCROLLAROUNDTHEMAP"));
		// Add players
		C4Player *pPlr; int32_t iIndex;
		for (iIndex=0; (pPlr = ::Players.GetByIndex(iIndex)); iIndex++)
		{
			// Ignore invisible
			if (!pPlr->IsInvisible())
			{
				// Symbol
				fctSymbol.Create(C4SymbolSize,C4SymbolSize);
				::GraphicsResource.fctPlayerClr.DrawClr(fctSymbol, true, pPlr->ColorDw);
				// Message
				StdStrBuf sMsg;
				DWORD dwClr = pPlr->ColorDw;
				sMsg.Format("<c %x>%s</c>", (unsigned int)C4GUI::MakeColorReadableOnBlack(dwClr), pPlr->GetName());
				// Command
				StdStrBuf sCommand;
				sCommand.Format("Observe:%d", (int)pPlr->Number);
				// Info caption
				StdStrBuf sInfo;
				sInfo.Format(LoadResStr("IDS_TEXT_FOLLOWVIEWOFPLAYER"), pPlr->GetName());
				// Add item
				Add(sMsg.getData(),fctSymbol,sCommand.getData(),C4MN_Item_NoCount,NULL,sInfo.getData());
				fctSymbol.Default();
				// check if this is the currently selected player
				if (pVP->GetPlayer() == pPlr->Number) iInitialSelection = GetItemCount()-1;
			}
			// Initial selection on followed player
			if (fWasEmpty) SetSelection(iInitialSelection, false, true);
		}
	}
	break;
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	default:
		// No internal refill needed
		return true;
		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	}

	// Successfull internal refill
	rfRefilled = true;
	return true;
}