1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2005, Sven2
6 * Copyright (c) 2017-2021, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// player team management for teamwork melees
19
20#pragma once
21
22#include "C4Constants.h"
23#include "C4ForwardDeclarations.h"
24#include "StdBuf.h"
25
26#include <cstdint>
27
28class C4TeamList;
29
30// class predec
31namespace C4GUI { class ComboBox_FillCB; }
32
33// constant used by lobby to indicate invisible, random team
34const int32_t TEAMID_Unknown = -1;
35
36// constant used by InitScenarioPlayer() to indicate creation of a new team
37const int32_t TEAMID_New = -1;
38
39// one player team
40class C4Team
41{
42private:
43 // std::vector...
44 // containing player info IDs
45 int32_t *piPlayers;
46 int32_t iPlayerCount;
47 int32_t iPlayerCapacity;
48
49public:
50 // copying
51 C4Team(const C4Team &rCopy);
52 C4Team &operator=(const C4Team &rCopy);
53
54protected:
55 // team identification; usually > 0 for a valid team
56 int32_t iID;
57 char Name[C4MaxName + 1];
58 int32_t iPlrStartIndex; // 0 for unassigned; 1 to 4 if all players of that team shall be assigned a specific [Player*]-section in the Scenario.txt
59 uint32_t dwClr; // team color
60 StdStrBuf sIconSpec; // icon drawing specification for offline or runtime team selection dialog
61 int32_t iMaxPlayer; // maximum number of players allowed in this team - 0 for infinite
62
63 friend class C4TeamList;
64
65public:
66 C4Team() : piPlayers(nullptr), iPlayerCount(0), iPlayerCapacity(0), iID(0), iPlrStartIndex(0), dwClr(0), iMaxPlayer(0) { *Name = 0; }
67 ~C4Team() { Clear(); }
68
69 void Clear();
70 void AddPlayer(class C4PlayerInfo &rInfo, bool fAdjustPlayer); // add player by info; adjusts ID in info and at any joined player
71 void RemoveIndexedPlayer(int32_t iIndex); // remove info at index; this changes the local list only
72 void RemovePlayerByID(int32_t iID);
73
74 int32_t GetPlayerCount() const { return iPlayerCount; }
75 const char *GetName() const { return Name; }
76 int32_t GetID() const { return iID; }
77 bool IsPlayerIDInTeam(int32_t iID); // search list for a player with the given ID
78 int32_t GetFirstUnjoinedPlayerID() const; // search for a player that does not have the join-flag set
79 int32_t GetFirstActivePlayerID() const; // search for a player that is still in the game
80 int32_t GetPlrStartIndex() const { return iPlrStartIndex; }
81 uint32_t GetColor() const { return dwClr; }
82 const char *GetIconSpec() const { return sIconSpec.getData(); }
83 bool IsFull() const { return iMaxPlayer && (iPlayerCount >= iMaxPlayer); } // whether no more players may join this team
84 StdStrBuf GetNameWithParticipants() const; // compose team name like "Team 1 (boni, GhostBear, Clonko)"
85 bool HasWon() const; // return true if any member player of the team has won
86
87 int32_t GetIndexedPlayer(int32_t iIndex) const { return Inside<int32_t>(ival: iIndex, lbound: 0, rbound: iPlayerCount - 1) ? piPlayers[iIndex] : 0; }
88
89 void CompileFunc(StdCompiler *pComp);
90
91 // this rechecks teams for all (not removed) players; sets players here by team selection in player infos
92 void RecheckPlayers();
93
94 // this assigns a team color if it's still zero
95 void RecheckColor(C4TeamList &rForList);
96};
97
98// global team list
99class C4TeamList
100{
101public:
102 // team config constants
103 enum ConfigValue
104 {
105 TEAM_None = 0,
106 TEAM_Custom = 1,
107 TEAM_Active = 2,
108 TEAM_AllowHostilityChange = 3,
109 TEAM_Dist = 4,
110 TEAM_AllowTeamSwitch = 5,
111 TEAM_AutoGenerateTeams = 6,
112 TEAM_TeamColors = 7,
113 };
114
115 // team distribution configuration
116 enum TeamDist
117 {
118 TEAMDIST_First = 0,
119 TEAMDIST_Free = 0, // anyone can choose teams
120 TEAMDIST_Host = 1, // host decides teams
121 TEAMDIST_None = 2, // no teams
122 TEAMDIST_Random = 3, // fixed random teams
123 TEAMDIST_RandomInv = 4, // fixed random teams invisible in lobby
124 TEAMDIST_Last = 4,
125 };
126
127private:
128 // std::vector...
129 C4Team **ppList;
130 int32_t iTeamCount;
131 int32_t iTeamCapacity;
132
133 int32_t iLastTeamID;
134 bool fAllowHostilityChange; // hostility not fixed
135 bool fAllowTeamSwitch; // teams not fixed
136 bool fActive;
137 bool fCustom; // set if read from team file or changed in scenario
138 bool fTeamColors; // if set, player colors are determined by team colors
139 bool fAutoGenerateTeams; // teams are generated automatically so there's enough teams for everyone
140 TeamDist eTeamDist;
141 int32_t iMaxScriptPlayers; // maximum number of script players to be added in the lobby
142 StdStrBuf sScriptPlayerNames; // default script player names
143 int32_t randomTeamCount; // count of teams used when generating random teams; unlimited if less than 2
144
145public:
146 C4TeamList() : ppList(nullptr), iTeamCount(0), iTeamCapacity(0), iLastTeamID(0), fAllowHostilityChange(true), fAllowTeamSwitch(false),
147 fActive(true), fCustom(false), eTeamDist(TEAMDIST_Free), fTeamColors(false), fAutoGenerateTeams(false), iMaxScriptPlayers(0) {}
148 ~C4TeamList() { Clear(); }
149 void Clear();
150
151 C4TeamList &operator=(const C4TeamList &rCopy);
152
153private:
154 // no copying
155 C4TeamList(const C4TeamList &rCopy);
156
157private:
158 void AddTeam(C4Team *pNewTeam); // add a team; grow list if necessary
159 void ClearTeams(); // delete all teams
160 bool GenerateDefaultTeams(int32_t iUpToID); // generate Team 1, Team 2, etc.
161 int32_t GetGenerateTeamCount() const;
162
163public:
164 C4Team *GetTeamByID(int32_t iID) const; // get team by ID
165 C4Team *GetGenerateTeamByID(int32_t iID); // get team by ID; generate if not existent. Always generate for TEAMID_New.
166 C4Team *GetTeamByIndex(int32_t iIndex) const; // get team by list index, to enumerate all teams
167 C4Team *GetTeamByPlayerID(int32_t iID) const; // get team by player ID (not number!)
168 int32_t GetLargestTeamID() const;
169 C4Team *GetRandomSmallestTeam(bool limitRandomTeamCount = false) const; // get team with least amount of players in it
170 int32_t GetTeamCount() const { return iTeamCount; }
171 int32_t GetRandomTeamCount() const { return randomTeamCount; }
172
173 C4Team *CreateTeam(const char *szName); // create a custom team
174
175 bool IsMultiTeams() const { return fActive; } // teams can be selected
176 bool IsCustom() const { return fCustom; } // whether teams are not generated as default
177 bool IsHostilityChangeAllowed() const { return fAllowHostilityChange; } // allow (temporary) hostility changes at runtime
178 bool IsTeamSwitchAllowed() const { return fAllowTeamSwitch; } // allow permanent team changes at runtime
179 bool CanLocalChooseTeam() const; // whether the local host can determine teams (e.g., not if teams are random, or if all except the player's current team are full)
180 bool CanLocalChooseTeam(int32_t idPlayer) const; // whether the local host can determine teams (e.g., not if teams are random, or if all except the player's current team are full)
181 bool CanLocalSeeTeam() const;
182 bool IsTeamColors() const { return fTeamColors; } // whether team colors are enabled
183 bool IsRandomTeam() const { return eTeamDist == TEAMDIST_Random || eTeamDist == TEAMDIST_RandomInv; } // whether a random team mode is selected
184 bool IsJoin2TeamAllowed(int32_t idTeam); // checks whether a team ID is valid and still available for new joins
185 bool IsAutoGenerateTeams() const { return fAutoGenerateTeams; }
186 bool IsRuntimeJoinTeamChoice() const { return IsCustom() && IsMultiTeams(); } // whether players joining at runtime must select a team first
187 int32_t GetMaxScriptPlayers() const { return iMaxScriptPlayers; } // return max number of script players to be added inthe lobby
188 int32_t GetForcedTeamSelection(int32_t idForPlayer) const; // if there's only one team for the player to join, return that team ID
189 StdStrBuf GetScriptPlayerName() const; // get a name to assign to a new script player. Try to avoid name conflicts
190 bool IsTeamVisible() const; // teams invisible during lobby time if TEAMDIST_RandomInv
191
192 void EnforceLeagueRules(); // enforce some league settings, such as disallowing runtime team change
193
194 // assign a team ID to player info; recheck if any user-set team infos are valid within the current team options
195 // creates a new team for melee; assigns the least-used team for teamwork melee
196 // host/single-call only; using SafeRandom!
197 bool RecheckPlayerInfoTeams(C4PlayerInfo &rNewJoin, bool fByHost);
198
199 // compiler
200 void CompileFunc(StdCompiler *pComp);
201 bool Load(C4Group &hGroup, class C4Scenario *pInitDefault, class C4LangStringTable *pLang); // clear self and load from group file (C4CFN_Teams); init default by scen if no team fiel is present
202 bool Save(C4Group &hGroup); // save to group file (C4CFN_Teams)
203
204 // this rechecks teams for all (not removed) players; sets players here by team selection in player infos
205 void RecheckPlayers();
206
207 // this reorders any unjoined players to teams if they are unevenly filled and team distribution is random
208 // any changed players will be flagged "updated"
209 void RecheckTeams();
210
211 // marks all unjoined players as not-in-team and reassigns a team for them
212 // also automatically flags all affected player infos as updated
213 void ReassignAllTeams();
214
215private:
216 // team distribution configuration
217 std::string GetTeamDistName(TeamDist eTeamDist) const;
218
219public:
220 void FillTeamDistOptions(C4GUI::ComboBox_FillCB *pFiller) const;
221 void SendSetTeamDist(TeamDist eNewDist);
222 TeamDist GetTeamDist() const { return eTeamDist; }
223 std::string GetTeamDistString() const;
224 bool HasTeamDistOptions() const;
225 void SetTeamDistribution(TeamDist eToVal);
226 void SendSetTeamColors(bool fEnabled);
227 void SetTeamColors(bool fEnabled);
228 void SetRandomTeamCount(int32_t count);
229};
230