| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2008, Sven2 |
| 6 | * Copyright (c) 2017-2020, 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 listbox used in lobby and game over dlg |
| 19 | |
| 20 | #pragma once |
| 21 | |
| 22 | #include "C4Gui.h" |
| 23 | #include "C4GuiListBox.h" |
| 24 | |
| 25 | #include "C4Scenario.h" |
| 26 | #include "C4Network2Res.h" |
| 27 | #include "C4GameLobby.h" |
| 28 | |
| 29 | class C4Team; |
| 30 | |
| 31 | class C4PlayerInfoListBox : public C4GUI::ListBox |
| 32 | { |
| 33 | public: |
| 34 | enum Spacings |
| 35 | { |
| 36 | // some spacings |
| 37 | IconLabelSpacing = 2, // space between an icon and its text |
| 38 | ClientListBoxSpacing = 8, // space between two clients in the list |
| 39 | PlayerListBoxIndent = 3, // indent of player list box items |
| 40 | |
| 41 | SoundIconShowTime = 1, // seconds. min time a sound icon is shown |
| 42 | }; |
| 43 | |
| 44 | // list box mode |
| 45 | enum Mode |
| 46 | { |
| 47 | PILBM_LobbyClientSort, |
| 48 | PILBM_LobbyTeamSort, |
| 49 | PILBM_Evaluation, |
| 50 | PILBM_EvaluationNoWinners, |
| 51 | }; |
| 52 | |
| 53 | private: |
| 54 | // generic player list item |
| 55 | class ListItem : public C4GUI::Window |
| 56 | { |
| 57 | protected: |
| 58 | C4PlayerInfoListBox *pList; |
| 59 | uint32_t dwBackground; // not drawn if 0 |
| 60 | |
| 61 | public: |
| 62 | struct ID |
| 63 | { |
| 64 | enum IDType |
| 65 | { |
| 66 | PLI_NONE = 0, |
| 67 | PLI_SCRIPTPLR, // script player caption (ID=0) - script players themselbed are regular PLI_PLAYER |
| 68 | PLI_SAVEGAMEPLR, // restore savegame player (ID>0), or caption (ID=0) |
| 69 | PLI_PLAYER, // player |
| 70 | PLI_CLIENT, // client label |
| 71 | PLI_TEAM, // team label |
| 72 | PLI_REPLAY, // replay player (ID>0), or caption (ID=0) |
| 73 | } idType; |
| 74 | |
| 75 | int32_t id; // player file ID or team ID or client ID |
| 76 | |
| 77 | ID() : idType(PLI_NONE), id(0) {} |
| 78 | ID(IDType eType, int32_t id) : idType(eType), id(id) {} |
| 79 | |
| 80 | inline bool operator==(const ID &r2) const |
| 81 | { |
| 82 | return idType == r2.idType && id == r2.id; |
| 83 | } |
| 84 | }; |
| 85 | |
| 86 | ID idListItemID; |
| 87 | |
| 88 | C4GameLobby::MainDlg *GetLobby() const; |
| 89 | virtual void Update() {} // periodic update callback |
| 90 | |
| 91 | protected: |
| 92 | virtual void DrawElement(C4FacetEx &cgo) override; // draw background |
| 93 | bool CanLocalChooseTeams(int32_t idPlayer = 0) const; // whether the local client can change any teams |
| 94 | |
| 95 | public: |
| 96 | ListItem(C4PlayerInfoListBox *pList) : pList(pList), dwBackground(0), C4GUI::Window() {} |
| 97 | }; |
| 98 | |
| 99 | // lobby information and display of joined players |
| 100 | class PlayerListItem : public ListItem |
| 101 | { |
| 102 | private: |
| 103 | // subcomponents |
| 104 | C4GUI::Icon *pIcon; // player icon |
| 105 | C4GUI::Label *pNameLabel; // label indicating player name |
| 106 | C4GUI::Label *pScoreLabel; // label showing some player score (league) |
| 107 | C4GUI::Label *pTimeLabel; // evaluation only: label showing total playing time |
| 108 | C4GUI::Label *; // evaluation only: label showing extra data set by script |
| 109 | C4GUI::Icon *pRankIcon; // league rank icon |
| 110 | C4GUI::ComboBox *pTeamCombo; // team selection combo - nullptr for no-team-scens |
| 111 | C4GUI::Picture *pTeamPic; // evaluation only: Team icon spec |
| 112 | |
| 113 | bool fIconSet; // whether custom icon has been set |
| 114 | bool fJoinedInfoSet; // join info for savegame recreation |
| 115 | uint32_t dwJoinClr, dwPlrClr; // colors currently reflected in icon |
| 116 | |
| 117 | protected: |
| 118 | int32_t idClient, idPlayer; // referenced IDs |
| 119 | bool fFreeSavegamePlayer; // if set, the player is an (unassociated) savegame player |
| 120 | bool fShownCollapsed; // true if small view is shown |
| 121 | |
| 122 | protected: |
| 123 | virtual void UpdateOwnPos() override; // recalculate item positioning |
| 124 | virtual int32_t GetListItemTopSpacing() override; |
| 125 | |
| 126 | public: |
| 127 | PlayerListItem(C4PlayerInfoListBox *pForListBox, int32_t idClient, int32_t idPlayer, bool fSavegamePlayer, C4GUI::Element *pInsertBeforeElement); |
| 128 | ~PlayerListItem() {} |
| 129 | |
| 130 | void UpdateIcon(C4PlayerInfo *pInfo, C4PlayerInfo *pJoinedInfo); // update player icon |
| 131 | void UpdateTeam(); |
| 132 | void UpdateScoreLabel(C4PlayerInfo *pInfo); // update league score labels and icons |
| 133 | void UpdateCollapsed(); |
| 134 | |
| 135 | public: |
| 136 | C4GUI::ContextMenu *OnContext(C4GUI::Element *pListItem, int32_t iX, int32_t iY); // open context menu |
| 137 | C4GUI::ContextMenu *OnContextTakeOver(C4GUI::Element *pListItem, int32_t iX, int32_t iY); // takeover savegame player submenu |
| 138 | void OnCtxTakeOver(C4GUI::Element *pListItem, const int32_t &idPlayer); |
| 139 | void OnCtxRemove(C4GUI::Element *pListItem); |
| 140 | void OnCtxNewColor(C4GUI::Element *pListItem); |
| 141 | |
| 142 | void OnTeamComboFill(C4GUI::ComboBox_FillCB *pFiller); |
| 143 | bool OnTeamComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection); |
| 144 | |
| 145 | virtual void Update() override; // update icon and team |
| 146 | |
| 147 | C4Network2Client *GetNetClient() const; // return associated network client |
| 148 | C4PlayerInfo *GetPlayerInfo() const; |
| 149 | |
| 150 | C4PlayerInfo *GetJoinedInfo() const; // if this player is joined or associated to a joined info, return the joined info |
| 151 | |
| 152 | bool IsLocalClientPlayer() const; // whether this player is going to join locally |
| 153 | bool CanLocalChooseTeam() const; // whether the local client can change team for this player |
| 154 | }; |
| 155 | |
| 156 | // lobby information and display of connected clients |
| 157 | class ClientListItem : public ListItem |
| 158 | { |
| 159 | private: |
| 160 | // subcomponents |
| 161 | C4GUI::Icon *pStatusIcon; // icon indicating client status (host, etc.) |
| 162 | C4GUI::Label *pNameLabel; // label indicating client name |
| 163 | C4GUI::Label *pPingLabel; // label indicating ping to client - may be nullptr |
| 164 | |
| 165 | protected: |
| 166 | int32_t idClient; // associated network interface ID |
| 167 | uint32_t dwClientClr; // client color used for chatting |
| 168 | |
| 169 | bool fIsShownActive; // whether client was active in last update |
| 170 | time_t tLastSoundTime; // now() when the client last issued a sound (display as sound icon). 0 for no sound. |
| 171 | |
| 172 | public: |
| 173 | ClientListItem(C4PlayerInfoListBox *pForListBox, const C4ClientCore &rClientInfo, ListItem *pInsertBefore); |
| 174 | ~ClientListItem() {} |
| 175 | |
| 176 | void SetColor(uint32_t dwToClr) // update color of client name label |
| 177 | { |
| 178 | pNameLabel->SetColor(dwToClr: (dwClientClr = dwToClr) | C4GUI_MessageFontAlpha); |
| 179 | } |
| 180 | |
| 181 | void SetStatus(C4GUI::Icons icoNewStatus) // set new status |
| 182 | { |
| 183 | pStatusIcon->SetIcon(icoNewStatus); |
| 184 | } |
| 185 | |
| 186 | void SetPing(int32_t iToPing); // update ping label; iToPing=-1 removes the label |
| 187 | void SetSoundIcon(); // sets the sound icon as current icon and schedules reset after some time |
| 188 | |
| 189 | // spacing inserted between two client list items |
| 190 | virtual int32_t GetListItemTopSpacing() override { return ClientListBoxSpacing; } |
| 191 | virtual bool GetListItemTopSpacingBar() override { return true; } |
| 192 | |
| 193 | public: |
| 194 | void UpdateInfo(); // update for changed player info |
| 195 | |
| 196 | uint32_t GetColor() const { return dwClientClr; } // client chat color |
| 197 | C4Client *GetClient() const; // get client associated with this list item |
| 198 | bool IsLocalClientPlayer() const; // whether this player is going to join locally |
| 199 | C4GUI::Icons GetCurrentStatusIcon(); // get status icon that shows the current client state |
| 200 | class C4Network2Client *GetNetClient() const; // return assicuated network client; nullptr for local |
| 201 | |
| 202 | virtual void Update() override { UpdatePing(); UpdateInfo(); } |
| 203 | void UpdatePing(); // update ping label |
| 204 | |
| 205 | C4GUI::ContextMenu *OnContext(C4GUI::Element *pListItem, int32_t iX, int32_t iY); // open context menu |
| 206 | void OnCtxKick(C4GUI::Element *pListItem); // kick item selected in client ctx menu |
| 207 | void OnCtxActivate(C4GUI::Element *pListItem); // toggle player/observer |
| 208 | void OnCtxInfo(C4GUI::Element *pListItem); // show info dlg (modal) |
| 209 | void OnCtxToggleMute(C4GUI::Element *pListItem); // toggle /sound mute/unmute |
| 210 | void OnBtnAddPlr(C4GUI::Control *btn); |
| 211 | }; |
| 212 | |
| 213 | // team label |
| 214 | class TeamListItem : public ListItem |
| 215 | { |
| 216 | private: |
| 217 | // subcomponents |
| 218 | C4GUI::Icon *pIcon; |
| 219 | C4GUI::Label *pNameLabel; |
| 220 | |
| 221 | int32_t idTeam; // team ID |
| 222 | |
| 223 | protected: |
| 224 | virtual void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse movement or buttons |
| 225 | virtual void UpdateOwnPos() override; // recalculate item positioning |
| 226 | |
| 227 | public: |
| 228 | TeamListItem(C4PlayerInfoListBox *pForListBox, int32_t idTeam, ListItem *pInsertBefore); |
| 229 | |
| 230 | // spacing inserted between of those list items; usually this is top anyway... |
| 231 | virtual bool GetListItemTopSpacingBar() override { return true; } |
| 232 | virtual int32_t GetListItemTopSpacing() override { return ClientListBoxSpacing; } |
| 233 | |
| 234 | virtual void Update() override; |
| 235 | |
| 236 | private: |
| 237 | void MoveLocalPlayersIntoTeam(); // move all local players into team marked by this label |
| 238 | }; |
| 239 | |
| 240 | // list of unassigned savegame recreation players |
| 241 | class FreeSavegamePlayersListItem : public ListItem |
| 242 | { |
| 243 | private: |
| 244 | // subcomponents |
| 245 | C4GUI::Icon *pIcon; |
| 246 | C4GUI::Label *pNameLabel; |
| 247 | |
| 248 | public: |
| 249 | FreeSavegamePlayersListItem(C4PlayerInfoListBox *pForListBox, ListItem *pInsertBefore); |
| 250 | |
| 251 | // spacing inserted between of those list items; usually this is top anyway... |
| 252 | virtual bool ListItemTopSpacingBar() { return true; } |
| 253 | virtual int32_t GetListItemTopSpacing() override { return ClientListBoxSpacing; } |
| 254 | |
| 255 | public: |
| 256 | virtual void Update() override; |
| 257 | }; |
| 258 | |
| 259 | // list of script players (both savegame recreation and regular) |
| 260 | class ScriptPlayersListItem : public ListItem |
| 261 | { |
| 262 | private: |
| 263 | // subcomponents |
| 264 | C4GUI::Icon *pIcon; |
| 265 | C4GUI::Label *pNameLabel; |
| 266 | C4GUI::IconButton *btnAddPlayer; |
| 267 | |
| 268 | public: |
| 269 | ScriptPlayersListItem(C4PlayerInfoListBox *pForListBox, ListItem *pInsertBefore); |
| 270 | |
| 271 | // spacing inserted between of those list items; usually this is top anyway... |
| 272 | virtual bool ListItemTopSpacingBar() { return true; } |
| 273 | virtual int32_t GetListItemTopSpacing() override { return ClientListBoxSpacing; } |
| 274 | |
| 275 | protected: |
| 276 | void OnBtnAddPlr(C4GUI::Control *btn); |
| 277 | |
| 278 | public: |
| 279 | virtual void Update() override; |
| 280 | }; |
| 281 | |
| 282 | // list of players in record (currently not used, because replays in network are not allowed :C) |
| 283 | class ReplayPlayersListItem : public ListItem |
| 284 | { |
| 285 | private: |
| 286 | // subcomponents |
| 287 | C4GUI::Icon *pIcon; |
| 288 | C4GUI::Label *pNameLabel; |
| 289 | |
| 290 | public: |
| 291 | ReplayPlayersListItem(C4PlayerInfoListBox *pForListBox, ListItem *pInsertBefore); |
| 292 | |
| 293 | // spacing inserted between of those list items; usually this is top anyway... |
| 294 | virtual bool GetListItemTopSpacingBar() override { return true; } |
| 295 | virtual int32_t GetListItemTopSpacing() override { return ClientListBoxSpacing; } |
| 296 | }; |
| 297 | |
| 298 | private: |
| 299 | Mode eMode; |
| 300 | int32_t iMaxUncollapsedPlayers; // maximum number of players that can be displayed without collapse - valid only if fIsCollapsed |
| 301 | bool fIsCollapsed; |
| 302 | int32_t iTeamFilter; // if nonzero, only playeers of this team are shown in the listbox |
| 303 | |
| 304 | uint32_t dwTextColor; |
| 305 | CStdFont *pCustomFont; |
| 306 | |
| 307 | enum AddMode |
| 308 | { |
| 309 | AM_Winners, |
| 310 | AM_Losers, |
| 311 | AM_All, |
| 312 | }; |
| 313 | |
| 314 | void UpdateSavegamePlayers(ListItem **ppCurrInList); |
| 315 | void UpdateReplayPlayers(ListItem **ppCurrInList); |
| 316 | void UpdateScriptPlayers(ListItem **ppCurrInList); |
| 317 | void UpdatePlayersByTeam(ListItem **ppCurrInList); |
| 318 | void UpdatePlayersByRandomTeam(ListItem **ppCurrInList); |
| 319 | void UpdatePlayersByClient(ListItem **ppCurrInList); |
| 320 | void UpdatePlayersByEvaluation(ListItem **ppCurrInList, bool fShowWinners); |
| 321 | void UpdatePlayersByEvaluation(ListItem **ppCurrInList, C4Team *pTeam, AddMode eAddMode); |
| 322 | |
| 323 | ListItem *GetPlayerListItem(ListItem::ID::IDType eType, int32_t id); // search for a player list item |
| 324 | bool PlrListItemUpdate(ListItem::ID::IDType eType, int32_t id, class ListItem **pEnsurePos); // search for player list item with given ID in the list starting at ensurepos; ensure it's positioned at given pos; update and return true if found |
| 325 | |
| 326 | bool IsEvaluation() const { return eMode == PILBM_Evaluation || eMode == PILBM_EvaluationNoWinners; } |
| 327 | bool IsLobby() const { return eMode == PILBM_LobbyClientSort || eMode == PILBM_LobbyTeamSort; } |
| 328 | bool IsTeamFilter() const { return !!iTeamFilter; } |
| 329 | |
| 330 | protected: |
| 331 | bool IsPlayerItemCollapsed(PlayerListItem *pItem); // CB from list item: return true if it should be shown small |
| 332 | void OnPlrListSelChange(class C4GUI::Element *pEl) { Update(); } |
| 333 | |
| 334 | uint32_t GetTextColor() const { return dwTextColor; } |
| 335 | CStdFont *GetCustomFont() const { return pCustomFont; } |
| 336 | |
| 337 | public: |
| 338 | C4PlayerInfoListBox(const C4Rect &rcBounds, Mode eMode, int32_t iTeamFilter = 0); |
| 339 | virtual ~C4PlayerInfoListBox() {} |
| 340 | |
| 341 | void Update(); // update player list |
| 342 | void SetClientSoundIcon(int32_t iForClientID); |
| 343 | void SetMode(Mode eNewMode); |
| 344 | |
| 345 | Mode GetMode() const { return eMode; } |
| 346 | |
| 347 | friend class PlayerListItem; |
| 348 | friend class TeamListItem; |
| 349 | }; |
| 350 | |