OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4GameLobby.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // the ingame-lobby
17 
18 #include "C4Include.h"
20 #include "gui/C4GameLobby.h"
21 
22 #include "game/C4Application.h"
23 #include "c4group/C4Components.h"
25 #include "gui/C4GameOptions.h"
26 #include "gui/C4ChatDlg.h"
28 #include "gui/C4MessageInput.h"
29 #include "game/C4Game.h"
30 #include "network/C4Network2.h"
32 #include "control/C4GameControl.h"
33 
34 namespace C4GameLobby
35 {
36 
37  bool UserAbort = false;
38 
39 // ----------- C4PacketCountdown ---------------------------------------------
40 
42  {
43  pComp->Value(mkNamingAdapt(iCountdown, "Countdown", 0));
44  }
45 
47  {
48  const char *szCountdownMsg;
49  if (iCountdown < AlmostStartCountdownTime && !fInitialMsg) szCountdownMsg = "%d..."; else szCountdownMsg = LoadResStr("IDS_PRC_COUNTDOWN");
50  return FormatString(szCountdownMsg, (int)iCountdown);
51  }
52 
53 
54 // ----------- C4PacketSetScenarioParameter ---------------------------------------------
55 
57  {
59  pComp->Value(mkNamingAdapt(Value, "Value", 0));
60  }
61 
62 
63 // ----------- ScenDescs ---------------------------------------------
64 
65  ScenDesc::ScenDesc(const C4Rect &rcBounds, bool fActive) : C4GUI::Window(), fDescFinished(false)
66  {
67  // build components
68  SetBounds(rcBounds);
69  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
70  AddElement(pDescBox = new C4GUI::TextWindow(caMain.GetAll(), 0, 0, 0, 100, 4096, "", true));
71  pDescBox->SetDecoration(false, false, nullptr, true);
72  // initial update to set current data
73  if (fActive) Activate();
74  }
75 
76  void ScenDesc::Update()
77  {
78  // scenario present?
80  if (!pRes) return; // something's wrong
83  pDescBox->ClearText(false);
84  if (pRes->isComplete())
85  {
86  C4Group ScenarioFile;
87  if (!ScenarioFile.Open(pRes->getFile()))
88  {
89  pDescBox->AddTextLine("scenario file load error", &rTextFont, C4GUI_MessageFontClr, false, true);
90  }
91  else
92  {
93  // load desc
94  C4ComponentHost DefDesc;
96  pDescBox->AddTextLine(DefDesc.GetData(), &rTextFont, C4GUI_MessageFontClr, false, true, &rTitleFont);
97  else
98  pDescBox->AddTextLine(Game.ScenarioTitle.getData(), &rTitleFont, C4GUI_CaptionFontClr, false, true);
99  }
100  // okay, done loading. No more updates.
101  fDescFinished = true;
102  Deactivate();
103  }
104  else
105  {
106  pDescBox->AddTextLine(FormatString(LoadResStr("IDS_MSG_SCENARIODESC_LOADING"), (int) pRes->getPresentPercent()).getData(),
107  &rTextFont, C4GUI_MessageFontClr, false, true);
108  }
109  pDescBox->UpdateHeight();
110  }
111 
113  {
114  // final desc set? no update then
115  if (fDescFinished) return;
116  // register timer if necessary
117  Application.Add(this);
118  // force an update
119  Update();
120  }
121 
123  {
124  // release timer if set
125  Application.Remove(this);
126  }
127 
128 // ----------- MainDlg -----------------------------------------------------------------------------
129 
131  : C4GUI::FullscreenDialog(!Game.ScenarioTitle ?
132  (const char *) LoadResStr("IDS_DLG_LOBBY"):
133  FormatString("%s - %s", Game.ScenarioTitle.getData(), LoadResStr("IDS_DLG_LOBBY")).getData(),
134  Game.ScenarioTitle.getData()),
135  pPlayerList(nullptr), pResList(nullptr), pChatBox(nullptr), pRightTabLbl(nullptr), pRightTab(nullptr),
136  pEdt(nullptr), btnRun(nullptr), btnPlayers(nullptr), btnResources(nullptr), btnTeams(nullptr), btnChat(nullptr)
137  {
138  // key bindings
139  pKeyHistoryUp = new C4KeyBinding(C4KeyCodeEx(K_UP ), "LobbyChatHistoryUp" , KEYSCOPE_Gui, new C4GUI::DlgKeyCBEx<MainDlg, bool>(*this, true , &MainDlg::KeyHistoryUpDown), C4CustomKey::PRIO_CtrlOverride);
140  pKeyHistoryDown= new C4KeyBinding(C4KeyCodeEx(K_DOWN), "LobbyChatHistoryDown", KEYSCOPE_Gui, new C4GUI::DlgKeyCBEx<MainDlg, bool>(*this, false, &MainDlg::KeyHistoryUpDown), C4CustomKey::PRIO_CtrlOverride);
141  // timer
142  Application.Add(this);
143  // indents / sizes
144  int32_t iDefBtnHeight = 32;
145  int32_t iIndentX1, iIndentX2, iIndentX3;
146  int32_t iIndentY1, iIndentY2, iIndentY3, iIndentY4;
147  int32_t iClientListWdt;
148  if (GetClientRect().Wdt > 500)
149  {
150  // normal dlg
151  iIndentX1 = 10; // lower button area
152  iIndentX2 = 20; // center area (chat)
153  iIndentX3 = 5; // client/player list
154  iClientListWdt = GetClientRect().Wdt / 3;
155  }
156  else
157  {
158  // small dlg
159  iIndentX1 = 2; // lower button area
160  iIndentX2 = 2; // center area (chat)
161  iIndentX3 = 1; // client/player list
162  iClientListWdt = GetClientRect().Wdt / 2;
163  }
164  if (GetClientRect().Hgt > 320)
165  {
166  // normal dlg
167  iIndentY1 = 16; // lower button area
168  iIndentY2 = 20; // status bar offset
169  iIndentY3 = 8; // center area (chat)
170  iIndentY4 = 8; // client/player list
171  }
172  else
173  {
174  // small dlg
175  iIndentY1 = 2; // lower button area
176  iIndentY2 = 2; // status bar offset
177  iIndentY3 = 1; // center area (chat)
178  iIndentY4 = 1; // client/player list
179  }
180  // set subtitle ToolTip
181  if (pSubTitle)
182  pSubTitle->SetToolTip(LoadResStr("IDS_DLG_SCENARIOTITLE"));
183  C4GUI::Label *pLbl;
184  // main screen components
185  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0,true);
186  caMain.GetFromBottom(iIndentY2);
187  // lower button-area
188  C4GUI::ComponentAligner caBottom(caMain.GetFromBottom(iDefBtnHeight+iIndentY1*2), iIndentX1,iIndentY1);
189  // add buttons
191  btnExit = new C4GUI::CallbackButton<MainDlg>(LoadResStr("IDS_DLG_EXIT"), caBottom.GetFromLeft(100), &MainDlg::OnExitBtn);
192  if (fHost)
193  {
194  btnRun = new C4GUI::CallbackButton<MainDlg>(LoadResStr("IDS_DLG_GAMEGO"), caBottom.GetFromRight(100), &MainDlg::OnRunBtn);
195  checkReady = nullptr;
196  }
197  else
198  {
199  checkReady = new C4GUI::CheckBox(caBottom.GetFromRight(90), LoadResStr("IDS_DLG_READY"), false);
201  caBottom.GetFromRight(90);
202  }
203  pGameOptionButtons = new C4GameOptionButtons(caBottom.GetCentered(caBottom.GetInnerWidth(), std::min<int32_t>(C4GUI_IconExHgt, caBottom.GetHeight())), true, fHost, true);
204 
205  // players / resources sidebar
206  C4GUI::ComponentAligner caRight(caMain.GetFromRight(iClientListWdt), iIndentX3,iIndentY4);
208  caRight.ExpandTop(iIndentY4*2 + 1); // undo margin, so client list is located directly under label
209  pRightTab = new C4GUI::Tabular(caRight.GetAll(), C4GUI::Tabular::tbNone);
210  C4GUI::Tabular::Sheet *pPlayerSheet = pRightTab->AddSheet(LoadResStr("IDS_DLG_PLAYERS"));
211  C4GUI::Tabular::Sheet *pResSheet = pRightTab->AddSheet(LoadResStr("IDS_DLG_RESOURCES"));
212  C4GUI::Tabular::Sheet *pOptionsSheet = pRightTab->AddSheet(LoadResStr("IDS_DLG_OPTIONS"));
213  C4GUI::Tabular::Sheet *pScenarioSheet = pRightTab->AddSheet(LoadResStr("IDS_DLG_SCENARIO"));
214  pPlayerList = new C4PlayerInfoListBox(pPlayerSheet->GetContainedClientRect(), C4PlayerInfoListBox::PILBM_LobbyClientSort);
215  pPlayerSheet->AddElement(pPlayerList);
216  pResList = new C4Network2ResDlg(pResSheet->GetContainedClientRect(), false);
217  pResSheet->AddElement(pResList);
218  pOptionsList = new C4GameOptionsList(pResSheet->GetContainedClientRect(), false, C4GameOptionsList::GOLS_Lobby);
219  pOptionsSheet->AddElement(pOptionsList);
220  pScenarioInfo = new ScenDesc(pResSheet->GetContainedClientRect(), false);
221  pScenarioSheet->AddElement(pScenarioInfo);
222  pRightTabLbl->SetContextHandler(new C4GUI::CBContextHandler<C4GameLobby::MainDlg>(this, &MainDlg::OnRightTabContext));
223  pRightTabLbl->SetClickFocusControl(pPlayerList);
224 
225  bool fHasTeams = Game.Teams.IsMultiTeams();
226  bool fHasChat = C4ChatDlg::IsChatActive();
227  int32_t iBtnNum = 4+fHasTeams+fHasChat;
228  if (fHasTeams)
229  {
230  btnTeams = new C4GUI::CallbackButton<MainDlg, C4GUI::IconButton>(C4GUI::Ico_Team, pRightTabLbl->GetToprightCornerRect(16, 16, 4, 4, --iBtnNum), LoadResStr("IDS_DLG_PLAYERSBYTEAM"), &MainDlg::OnTabTeams);
231  }
232  btnPlayers = new C4GUI::CallbackButton<MainDlg, C4GUI::IconButton>(C4GUI::Ico_Player, pRightTabLbl->GetToprightCornerRect(16,16,4,4,--iBtnNum), LoadResStr("IDS_DLG_PLAYERS"), &MainDlg::OnTabPlayers);
233  btnResources = new C4GUI::CallbackButton<MainDlg, C4GUI::IconButton>(C4GUI::Ico_Resource, pRightTabLbl->GetToprightCornerRect(16,16,4,4,--iBtnNum), LoadResStr("IDS_DLG_RESOURCES"), &MainDlg::OnTabRes);
234  btnOptions = new C4GUI::CallbackButton<MainDlg, C4GUI::IconButton>(C4GUI::Ico_Options, pRightTabLbl->GetToprightCornerRect(16,16,4,4,--iBtnNum), LoadResStr("IDS_DLG_OPTIONS"), &MainDlg::OnTabOptions);
235  btnScenario = new C4GUI::CallbackButton<MainDlg, C4GUI::IconButton>(C4GUI::Ico_Gfx, pRightTabLbl->GetToprightCornerRect(16,16,4,4,--iBtnNum), LoadResStr("IDS_DLG_SCENARIO"), &MainDlg::OnTabScenario);
236  if (fHasChat)
237  btnChat = new C4GUI::CallbackButton<MainDlg, C4GUI::IconButton>(C4GUI::Ico_Ex_Chat, pRightTabLbl->GetToprightCornerRect(16,16,4,4,--iBtnNum), LoadResStr("IDS_CTL_CHAT"), &MainDlg::OnBtnChat);
238 
239  // update labels and tooltips for player list
240  UpdateRightTab();
241 
242  // chat area
243  C4GUI::ComponentAligner caCenter(caMain.GetAll(), iIndentX2, iIndentY3);
244  // chat input box
245  C4GUI::ComponentAligner caChat(caCenter.GetFromBottom(C4GUI::Edit::GetDefaultEditHeight()), 0,0);
246  pLbl = new C4GUI::WoodenLabel(LoadResStr("IDS_CTL_CHAT"), caChat.GetFromLeft(40), C4GUI_CaptionFontClr, &::GraphicsResource.TextFont);
247  pEdt = new C4GUI::CallbackEdit<MainDlg>(caChat.GetAll(), this, &MainDlg::OnChatInput);
248  pEdt->SetToolTip(LoadResStr("IDS_DLGTIP_CHAT")); pLbl->SetToolTip(LoadResStr("IDS_DLGTIP_CHAT"));
249  pLbl->SetClickFocusControl(pEdt);
250  // log box
251  pChatBox = new C4GUI::TextWindow(caCenter.GetAll());
252  // add components in tab-order
253  AddElement(pChatBox);
254  AddElement(pLbl); AddElement(pEdt); // chat
255 
256  AddElement(pRightTabLbl);
257  if (btnTeams) AddElement(btnTeams);
258  AddElement(btnPlayers);
259  AddElement(btnResources);
260  AddElement(btnOptions);
261  AddElement(btnScenario);
262  if (btnChat) AddElement(btnChat);
263 
264  AddElement(pRightTab);
265  AddElement(btnExit); btnExit->SetToolTip(LoadResStr("IDS_DLGTIP_EXIT"));
266  AddElement(pGameOptionButtons);
267  if (fHost)
268  {
269  AddElement(btnRun);
270  btnRun->SetToolTip(LoadResStr("IDS_DLGTIP_GAMEGO"));
271  }
272  else
273  {
274  AddElement(checkReady);
275  checkReady->SetToolTip(LoadResStr("IDS_DLGTIP_READY"));
276  }
277  // set initial focus
278  SetFocus(pEdt, false);
279 
280  // stuff
281  eCountdownState = CDS_None;
282  iBackBufferIndex = -1;
283 
284  // initial player list update
285  UpdatePlayerList();
286  }
287 
289  {
290  Application.Remove(this);
291  delete pKeyHistoryUp;
292  delete pKeyHistoryDown;
293  }
294 
296  {
297  // abort dlg
298  Close(false);
299  }
300 
302  {
303  bool rIsOn = static_cast<C4GUI::CheckBox *>(pCheckBox)->GetChecked();
305  }
306 
307  void MainDlg::SetCountdownState(CountdownState eToState, int32_t iTimer)
308  {
309  // no change?
310  if (eToState == eCountdownState) return;
311  // changing away from countdown?
312  if (eCountdownState == CDS_Countdown)
313  {
314  StopSoundEffect("Structures::Elevator::Moving", nullptr);
315  if (eToState != CDS_Start) StartSoundEffect("Liquids::Pshshsh");
316  }
317  // change to game start?
318  if (eToState == CDS_Start)
319  {
320  // announce it!
321  StartSoundEffect("Fire::Blast3");
322  }
323  else if (eToState == CDS_Countdown)
324  {
325  StartSoundEffect("Fire::Fuse");
326  }
327  if (eToState == CDS_Countdown || eToState == CDS_LongCountdown)
328  {
329  // game start notify
331  if (!eCountdownState)
332  {
333  // host update start button to be abort button
334  if (btnRun) btnRun->SetText(LoadResStr("IDS_DLG_CANCEL"));
335  }
336  }
337  // countdown abort?
338  if (!eToState)
339  {
340  // host update start button to be start button again
341  if (btnRun) btnRun->SetText(LoadResStr("IDS_DLG_GAMEGO"));
342  // countdown abort message
343  OnLog(LoadResStr("IDS_PRC_STARTABORTED"), C4GUI_LogFontClr2);
344  }
345  // set new state
346  eCountdownState = eToState;
347  // update stuff (makes team sel and fair crew btn available)
348  pGameOptionButtons->SetCountdown(IsCountdown());
349  UpdatePlayerList();
350  }
351 
353  {
354  // determine new countdown state
355  int32_t iTimer = 0;
356  CountdownState eNewState;
357  if (Pkt.IsAbort())
358  eNewState = CDS_None;
359  else
360  {
361  iTimer = Pkt.GetCountdown();
362  if (!iTimer)
363  eNewState = CDS_Start; // game is about to be started (boom)
364  else if (iTimer <= AlmostStartCountdownTime)
365  eNewState = CDS_Countdown; // eToState
366  else
367  eNewState = CDS_LongCountdown;
368  }
369  bool fWasCountdown = !!eCountdownState;
370  SetCountdownState(eNewState, iTimer);
371  // display countdown (except last, which ends the lobby anyway)
372  if (iTimer)
373  {
374  // first countdown message
375  OnLog(Pkt.GetCountdownMsg(!fWasCountdown).getData(), C4GUI_LogFontClr2);
376  StartSoundEffect("UI::Tick");
377  }
378  }
379 
381  {
382  // flag as countdown if countdown running or game is about to start
383  // so team choice, etc. will not become available in the last split-second
384  return eCountdownState >= CDS_Countdown;
385  }
386 
387  void MainDlg::OnClosed(bool fOK)
388  {
389  // lobby aborted by user: remember not to display error log
390  if (!fOK)
391  C4GameLobby::UserAbort = true;
392  // finish countdown if running
393  // (may not be finished if status change packet from host is faster than the countdown-initiate)
394  if (eCountdownState) SetCountdownState(fOK ? CDS_Start : CDS_None, 0);
395  }
396 
398  {
399  // only for host
400  if (!::Network.isHost()) return;
401  // already started? then abort
402  if (eCountdownState) { ::Network.AbortLobbyCountdown(); return; }
403  // otherwise start, utilizing correct countdown time
405  }
406 
407  void MainDlg::Start(int32_t iCountdownTime)
408  {
409  // network savegame resumes: Warn if not all players have been associated
410  if (Game.C4S.Head.SaveGame)
412  {
413  StdStrBuf sMsg; sMsg.Ref(LoadResStr("IDS_MSG_NOTALLSAVEGAMEPLAYERSHAVE"));
414  if (!GetScreen()->ShowMessageModal(sMsg.getData(), LoadResStr("IDS_MSG_FREESAVEGAMEPLRS"), C4GUI::MessageDialog::btnYesNo, C4GUI::Ico_SavegamePlayer, &Config.Startup.HideMsgPlrNoTakeOver))
415  return;
416  }
417  // validate countdown time
418  iCountdownTime = ValidatedCountdownTime(iCountdownTime);
419  // either direct start...
420  if (!iCountdownTime)
421  ::Network.Start();
422  else
423  // ...or countdown
424  ::Network.StartLobbyCountdown(iCountdownTime);
425  }
426 
427  C4GUI::Edit::InputResult MainDlg::OnChatInput(C4GUI::Edit *edt, bool fPasting, bool fPastingMore)
428  {
429  // get edit text
430  C4GUI::Edit *pEdt = reinterpret_cast<C4GUI::Edit *>(edt);
431  const char *szInputText = pEdt->GetText();
432  // no input?
433  if (!szInputText || !*szInputText)
434  {
435  // do some error sound then
436  C4GUI::GUISound("UI::Error");
437  }
438  else
439  {
440  // store input in backbuffer before processing commands
441  // because those might kill the edit field
442  ::MessageInput.StoreBackBuffer(szInputText);
443  ::MessageInput.ProcessInput(szInputText);
444  }
445  // clear edit field after text has been processed
446  pEdt->SelectAll(); pEdt->DeleteSelection();
447  // reset backbuffer-index of chat history
448  iBackBufferIndex = -1;
449  // OK, on we go. Leave edit intact
450  return C4GUI::Edit::IR_None;
451  }
452 
453  void MainDlg::OnClientJoin(C4Client *pNewClient)
454  {
455  // update list
456  UpdatePlayerList();
457  }
458 
460  {
461  }
462 
463  void MainDlg::OnClientPart(C4Client *pPartClient)
464  {
465  // update list
466  UpdatePlayerList();
467  }
468 
469  void MainDlg::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2Client *pClient)
470  {
471  // note that player info update packets are not handled by this function,
472  // but by player info list and then forwarded to MainDlg::OnPlayerUpdate
473  // this is necessary because there might be changes (e.g. duplicate colors)
474  // done by player info list
475  // besides, this releases the lobby from doing any host/client-specializations
476 #define GETPKT(type, name) \
477  assert(pPacket); const type &name = \
478  static_cast<const type &>(*pPacket);
479  switch (cStatus)
480  {
481  case PID_LobbyCountdown: // initiate or abort countdown
482  {
484  // do countdown
485  OnCountdownPacket(Pkt);
486  }
487  break;
488  case PID_SetScenarioParameter: // set a scenario parameter value
489  {
491  ::Game.Parameters.ScenarioParameters.SetValue(Pkt.GetID(), Pkt.GetValue(), false);
492  // reflect updated value immediately on clients
493  if (pRightTab->GetActiveSheetIndex() == SheetIdx_Options) if (pOptionsList) pOptionsList->Update();
494  }
495  };
496 #undef GETPKT
497  }
498 
499  bool MainDlg::OnMessage(C4Client *pOfClient, const char *szMessage)
500  {
501  // output message should be prefixed with client already
502  const char *szMsgBuf = szMessage;
503  // 2do: log with player colors?
504  if (pChatBox && !pOfClient->IsIgnored())
505  {
506  pChatBox->AddTextLine(szMsgBuf, &::GraphicsResource.TextFont, ::Network.Players.GetClientChatColor(pOfClient ? pOfClient->getID() : Game.Clients.getLocalID(), true) | C4GUI_MessageFontAlpha, true, true);
507  pChatBox->ScrollToBottom();
508  }
509  // log it
510  LogSilent(szMsgBuf);
511  // done, success
512  return true;
513  }
514 
516  {
517  // show that someone played a sound
518  if (pOfClient && pPlayerList)
519  {
520  pPlayerList->SetClientSoundIcon(pOfClient->getID());
521  }
522  }
523 
524  void MainDlg::OnLog(const char *szLogMsg, DWORD dwClr)
525  {
526  if (pChatBox)
527  {
528  pChatBox->AddTextLine(szLogMsg, &::GraphicsResource.TextFont, dwClr, true, true);
529  pChatBox->ScrollToBottom();
530  }
531  }
532 
533  void MainDlg::OnError(const char *szErrMsg)
534  {
535  if (pChatBox)
536  {
537  StartSoundEffect("UI::Error");
538  pChatBox->AddTextLine(szErrMsg, &::GraphicsResource.TextFont, C4GUI_ErrorFontClr, true, true);
539  pChatBox->ScrollToBottom();
540  }
541  }
542 
544  {
545  UpdatePlayerList();
546  }
547 
548  void MainDlg::UpdatePlayerList()
549  {
550  // this updates ping label texts and teams
551  if (pPlayerList) pPlayerList->Update();
552  }
553 
555  {
556  // create context menu
557  C4GUI::ContextMenu *pMenu = new C4GUI::ContextMenu();
558  // players/resources
559  C4GUI::Tabular::Sheet *pPlayerSheet = pRightTab->GetSheet(0);
560  C4GUI::Tabular::Sheet *pResSheet = pRightTab->GetSheet(1);
561  C4GUI::Tabular::Sheet *pOptionsSheet = pRightTab->GetSheet(2);
562  pMenu->AddItem(pPlayerSheet->GetTitle(), pPlayerSheet->GetToolTip(), C4GUI::Ico_Player,
564  if (Game.Teams.IsMultiTeams())
565  {
566  StdCopyStrBuf strShowTeamsDesc(LoadResStr("IDS_MSG_SHOWTEAMS_DESC"));
567  pMenu->AddItem(LoadResStr("IDS_MSG_SHOWTEAMS"), strShowTeamsDesc.getData(), C4GUI::Ico_Team,
568  new C4GUI::CBMenuHandler<MainDlg>(this, &MainDlg::OnCtxTabTeams));
569  }
570  pMenu->AddItem(pResSheet->GetTitle(), pResSheet->GetToolTip(), C4GUI::Ico_Resource,
571  new C4GUI::CBMenuHandler<MainDlg>(this, &MainDlg::OnCtxTabRes));
572  pMenu->AddItem(pOptionsSheet->GetTitle(), pOptionsSheet->GetToolTip(), C4GUI::Ico_Options,
573  new C4GUI::CBMenuHandler<MainDlg>(this, &MainDlg::OnCtxTabOptions));
574  // open it
575  return pMenu;
576  }
577 
578  void MainDlg::OnClientAddPlayer(const char *szFilename, int32_t idClient)
579  {
580  // check client number
581  if (idClient != Game.Clients.getLocalID())
582  {
583  LobbyError(FormatString(LoadResStr("IDS_ERR_JOINPLR_NOLOCALCLIENT"), szFilename, idClient).getData());
584  return;
585  }
586  // player join - check filename
587  if (!ItemExists(szFilename))
588  {
589  LobbyError(FormatString(LoadResStr("IDS_ERR_JOINPLR_NOFILE"), szFilename).getData());
590  return;
591  }
592  // join!
594  }
595 
597  {
598  if (pPlayerList) pPlayerList->SetMode(C4PlayerInfoListBox::PILBM_LobbyClientSort);
599  if (pRightTab)
600  {
601  pRightTab->SelectSheet(SheetIdx_PlayerList, true);
602  UpdateRightTab();
603  }
604  }
605 
607  {
608  if (pPlayerList) pPlayerList->SetMode(C4PlayerInfoListBox::PILBM_LobbyTeamSort);
609  if (pRightTab)
610  {
611  pRightTab->SelectSheet(SheetIdx_PlayerList, true);
612  UpdateRightTab();
613  }
614  }
615 
617  {
618  if (pRightTab)
619  {
620  pRightTab->SelectSheet(SheetIdx_Res, true);
621  UpdateRightTab();
622  }
623  }
624 
626  {
627  if (pRightTab)
628  {
629  pRightTab->SelectSheet(SheetIdx_Options, true);
630  UpdateRightTab();
631  }
632  }
633 
635  {
636  if (pRightTab)
637  {
638  pRightTab->SelectSheet(SheetIdx_Scenario, true);
639  UpdateRightTab();
640  }
641  }
642 
644  {
645  if (!pRightTabLbl || !pRightTab || !pRightTab->GetActiveSheet() || !pPlayerList) return;
646  // copy active sheet data to label
647  pRightTabLbl->SetText(pRightTab->GetActiveSheet()->GetTitle());
648  pRightTabLbl->SetToolTip(pRightTab->GetActiveSheet()->GetToolTip());
649  // update
650  if (pRightTab->GetActiveSheetIndex() == SheetIdx_PlayerList) UpdatePlayerList();
651  if (pRightTab->GetActiveSheetIndex() == SheetIdx_Res) pResList->Activate(); else pResList->Deactivate();
652  if (pRightTab->GetActiveSheetIndex() == SheetIdx_Options) pOptionsList->Activate(); else pOptionsList->Deactivate();
653  if (pRightTab->GetActiveSheetIndex() == SheetIdx_Scenario) pScenarioInfo->Activate(); else pScenarioInfo->Deactivate();
654  // update selection buttons
655  if (btnPlayers) btnPlayers->SetHighlight(pRightTab->GetActiveSheetIndex() == SheetIdx_PlayerList && pPlayerList->GetMode() == C4PlayerInfoListBox::PILBM_LobbyClientSort);
656  if (btnResources) btnResources->SetHighlight(pRightTab->GetActiveSheetIndex() == SheetIdx_Res);
657  if (btnTeams) btnTeams->SetHighlight(pRightTab->GetActiveSheetIndex() == SheetIdx_PlayerList && pPlayerList->GetMode() == C4PlayerInfoListBox::PILBM_LobbyTeamSort);
658  if (btnOptions) btnOptions->SetHighlight(pRightTab->GetActiveSheetIndex() == SheetIdx_Options);
659  if (btnScenario) btnScenario->SetHighlight(pRightTab->GetActiveSheetIndex() == SheetIdx_Scenario);
660  }
661 
663  {
664  // open chat dialog
666  }
667 
669  {
670  // chat input only
671  if (!IsFocused(pEdt)) return false;
672  pEdt->SelectAll(); pEdt->DeleteSelection();
673  const char *szPrevInput = ::MessageInput.GetBackBuffer(fUp ? (++iBackBufferIndex) : (--iBackBufferIndex));
674  if (!szPrevInput || !*szPrevInput)
675  iBackBufferIndex = -1;
676  else
677  {
678  pEdt->InsertText(szPrevInput, true);
679  pEdt->SelectAll();
680  }
681  return true;
682  }
683 
685  {
686  // if the password setting has changed, make sure the buttons reflect this change
687  pGameOptionButtons->UpdatePasswordBtn();
688  }
689 
690  int32_t MainDlg::ValidatedCountdownTime(int32_t iTimeout)
691  {
692  // no negative timeouts
693  if (iTimeout < 0) iTimeout = 5;
694  // in leage mode, there must be at least five seconds timeout
695  if (Game.Parameters.isLeague() && iTimeout < 5) iTimeout = 5;
696  return iTimeout;
697  }
698 
700  {
701  pChatBox->ClearText(true);
702  }
703 
704  void LobbyError(const char *szErrorMsg)
705  {
706  // get lobby
707  MainDlg *pLobby = ::Network.GetLobby();
708  if (pLobby) pLobby->OnError(szErrorMsg);
709  else Log(szErrorMsg);
710  }
711 
712 
713  /* Countdown */
714 
715  Countdown::Countdown(int32_t iStartTimer) : iStartTimer(iStartTimer)
716  {
717  // only on network hosts
718  assert(::Network.isHost());
719  // ctor: Init; sends initial countdown packet
720  C4PacketCountdown pck(iStartTimer);
722  // also process on host
723  MainDlg *pLobby = ::Network.GetLobby();
724  if (pLobby)
725  {
726  pLobby->OnCountdownPacket(pck);
727  }
728  else
729  {
730  // no lobby: Message to log for dedicated/console hosts
731  Log(pck.GetCountdownMsg().getData());
732  }
733 
734  // init timer callback
735  Application.Add(this);
736  }
737 
739  {
740  // release timer
741  Application.Remove(this);
742  }
743 
745  {
746  // count down
747  iStartTimer = std::max<int32_t>(iStartTimer - 1, 0);
748  // only send "important" start timer numbers to all clients
749  if (iStartTimer <= AlmostStartCountdownTime || // last seconds
750  (iStartTimer <= 600 && !(iStartTimer % 10)) || // last minute: 10s interval
751  !(iStartTimer % 60)) // otherwise, minute interval
752  {
753  C4PacketCountdown pck(iStartTimer);
755  // also process on host
756  MainDlg *pLobby = ::Network.GetLobby();
757  if (pLobby)
758  pLobby->OnCountdownPacket(pck);
759  else if (iStartTimer)
760  {
761  // no lobby: Message to log for dedicated/console hosts
762  Log(pck.GetCountdownMsg().getData());
763  }
764  }
765  // countdown done
766  if (!iStartTimer)
767  {
768 #ifdef USE_CONSOLE
769  // Dedicated server: if there are not enough players for this game, abort and quit the application
771  || ::Network.Clients.Count() <= 2)
772  {
773  Log(LoadResStr("IDS_MSG_NOTENOUGHPLAYERSFORTHISRO")); // it would also be nice to send this message to all clients...
774  Application.Quit();
775  }
776  // Start the game
777  else
778 #endif // USE_CONSOLE
779  ::Network.Start();
780  }
781  }
782 
784  {
785  // host sends packets
786  if (!::Network.isHost()) return;
789  // also process on host
790  MainDlg *pLobby = ::Network.GetLobby();
791  if (pLobby)
792  {
793  pLobby->OnCountdownPacket(pck);
794  }
795  else
796  {
797  // no lobby: Message to log for dedicated/console hosts
798  Log(LoadResStr("IDS_PRC_STARTABORTED"));
799  }
800  }
801 
802 } // end of namespace
803 
804 
const char * getData() const
Definition: StdBuf.h:450
const char * GetTitle()
Definition: C4Gui.h:1624
virtual void CompileFunc(StdCompiler *pComp)
Definition: C4GameLobby.cpp:41
class C4GameLobby::MainDlg * GetLobby() const
Definition: C4Network2.h:216
bool fHost
Definition: TstC4NetIO.cpp:34
void AddItem(const char *szText, const char *szToolTip=nullptr, Icons icoIcon=Ico_None, MenuHandler *pMenuHandler=nullptr, ContextHandler *pSubmenuHandler=nullptr)
Definition: C4Gui.h:1873
C4PlayerInfoList & RestorePlayerInfos
Definition: C4Game.h:74
bool Start()
Definition: C4Network2.cpp:528
void OnClientPart(C4Client *pPartClient)
#define C4GUI_CaptionFontClr
Definition: C4Gui.h:37
C4Config Config
Definition: C4Config.cpp:831
#define GETPKT(type, name)
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1178
C4ConfigStartup Startup
Definition: C4Config.h:261
void GetAll(C4Rect &rcOut)
Definition: C4Gui.cpp:1128
void SetCountdown(bool fToVal)
bool LogSilent(const char *szMessage, bool fConsole)
Definition: C4Log.cpp:117
void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll)
Definition: C4Gui.h:1752
bool isHost() const
Definition: C4Network2.h:209
const char * GetBackBuffer(int32_t iIndex)
#define C4GUI_MessageFontClr
Definition: C4Gui.h:43
void AbortLobbyCountdown()
int32_t GetPlayerCount() const
void DeleteSelection()
Definition: C4GuiEdit.cpp:149
C4Game Game
Definition: C4Globals.cpp:52
C4GUI::Edit::InputResult OnChatInput(C4GUI::Edit *edt, bool fPasting, bool fPastingMore)
bool KeyHistoryUpDown(bool fUp)
C4Scenario C4S
Definition: C4Game.h:76
C4ConfigGeneral General
Definition: C4Config.h:252
StdCopyStrBuf ScenarioTitle
Definition: C4Game.h:105
const char * GetData() const
void OnClientJoin(C4Client *pNewClient)
ScenDesc(const C4Rect &rcBounds, bool fActive)
Definition: C4GameLobby.cpp:65
#define C4GUI_IconExHgt
Definition: C4Gui.h:96
bool isComplete() const
void OnClosed(bool fOK)
bool AddElement(Element *pChild, int32_t iIndent=0)
void SetToolTip(const char *szNewTooltip, bool is_immediate=false)
Definition: C4Gui.cpp:410
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2Client *pClient)
void SelectAll()
Definition: C4GuiEdit.cpp:641
bool BroadcastMsgToClients(const C4NetIOPacket &rPkt)
void Close(bool fOK)
const char * GetText()
Definition: C4Gui.h:1338
void OnCountdownPacket(const C4PacketCountdown &Pkt)
Definition: C4Rect.h:29
virtual C4Rect & GetClientRect()
Definition: C4Gui.h:864
C4MessageInput MessageInput
void AddElement(Element *pChild)
bool IsMultiTeams() const
Definition: C4Teams.h:163
void StopSoundEffect(const char *szSndName, C4Object *pObj)
int32_t HideMsgPlrNoTakeOver
Definition: C4Config.h:187
C4Rect GetContainedClientRect()
Definition: C4Gui.h:448
int32_t GetMinPlayer()
Definition: C4Scenario.cpp:141
C4GraphicsResource GraphicsResource
void SelectSheet(int32_t iIndex, bool fByUser)
C4GameParameters & Parameters
Definition: C4Game.h:69
void Add(StdSchedulerProc *pProc)
C4TeamList & Teams
Definition: C4Game.h:72
void OnLog(const char *szLogMsg, DWORD dwClr=C4GUI_LogFontClr)
const char * GetToolTip()
Definition: C4Gui.cpp:424
void StartLobbyCountdown(int32_t iCountdownTime)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
void OnClientAddPlayer(const char *szFilename, int32_t idClient)
void LobbyError(const char *szErrorMsg)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:93
int32_t getID() const
Definition: C4Client.h:105
bool OnMessage(C4Client *pOfClient, const char *szMessage)
C4SoundInstance * StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
int32_t Wdt
Definition: C4Rect.h:32
static bool LoadComponentHost(C4ComponentHost *host, C4Group &hGroup, const char *szFilename, const char *szLanguage)
Definition: C4Language.cpp:235
void OnClientSound(C4Client *pOfClient)
const char * getFile() const
bool SaveGame
Definition: C4Scenario.h:71
void OnCtxTabPlayers(C4GUI::Element *pListItem)
Definition: C4GameLobby.h:118
void StoreBackBuffer(const char *szMessage)
virtual void Quit()
C4Network2 Network
Definition: C4Globals.cpp:53
bool InsertText(const char *szText, bool fUser)
Definition: C4GuiEdit.cpp:163
static int32_t GetDefaultHeight(CStdFont *pUseFont=nullptr)
C4ScenarioParameters ScenarioParameters
DWORD GetClientChatColor(int idForClient, bool fLobby) const
int32_t getLocalID() const
Definition: C4Client.h:171
C4GameControl Control
C4NetIOPacket MkC4NetIOPacket(char cStatus, const class C4PacketBase &Pkt, const C4NetIO::addr_t &addr=C4NetIO::addr_t())
Definition: C4PacketBase.h:40
C4ConfigLobby Lobby
Definition: C4Config.h:257
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
void OnRunBtn(C4GUI::Control *btn)
C4PlayerInfo * FindUnassociatedRestoreInfo(const C4PlayerInfoList &rRestoreInfoList)
void OnCtxTabRes(C4GUI::Element *pListItem)
Definition: C4GameLobby.h:122
void OnTabRes(C4GUI::Control *btn)
void UpdateHeight()
Definition: C4Gui.h:1750
void OnError(const char *szErrMsg)
#define C4GUI_MessageFontAlpha
Definition: C4Gui.h:44
void OnTabScenario(C4GUI::Control *btn)
Sheet * GetSheet(int32_t iIndex)
Definition: C4Gui.h:1706
void Start(int32_t iCountdownTime)
void OnReadyCheck(C4GUI::Element *pCheckBox)
static int32_t GetDefaultEditHeight()
Definition: C4GuiEdit.cpp:113
int32_t CountdownTime
Definition: C4Config.h:198
void Value(const T &rStruct)
Definition: StdCompiler.h:170
void ScrollToBottom()
Definition: C4Gui.h:1742
char LanguageEx[CFG_MaxString+1]
Definition: C4Config.h:38
int32_t GetCountdown() const
Definition: C4GameLobby.h:44
C4GUI::ContextMenu * OnRightTabContext(C4GUI::Element *pLabel, int32_t iX, int32_t iY)
void OnClientConnect(C4Client *pClient, C4Network2IOConnection *pConn)
void Ref(const char *pnData)
Definition: StdBuf.h:463
void Remove(StdSchedulerProc *pProc)
#define C4GUI_LogFontClr2
Definition: C4Gui.h:52
void SetOnChecked(BaseCallbackHandler *pCB)
void OnTabTeams(C4GUI::Control *btn)
C4Network2ClientList Clients
Definition: C4Network2.h:116
C4PlayerInfoList & PlayerInfos
Definition: C4Game.h:73
void OnCtxTabOptions(C4GUI::Element *pListItem)
Definition: C4GameLobby.h:124
void SetFocus(Control *pCtrl, bool fByMouse)
StdStrBuf GetCountdownMsg(bool fInitialMsg=false) const
Definition: C4GameLobby.cpp:46
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
static C4ChatDlg * ShowChat()
Definition: C4ChatDlg.cpp:968
bool IsIgnored() const
Definition: C4Client.h:114
static bool IsChatActive()
Definition: C4ChatDlg.cpp:998
Sheet * GetActiveSheet()
Definition: C4Gui.h:1707
Countdown(int32_t iStartTimer)
#define C4CFN_ScenarioDesc
Definition: C4Components.h:88
void OnTabOptions(C4GUI::Control *btn)
const int ALeft
Definition: C4Surface.h:43
void NotifyUserIfInactive()
Definition: C4App.cpp:89
C4Network2Players Players
Definition: C4Network2.h:119
void OnTabPlayers(C4GUI::Control *btn)
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
C4ClientList & Clients
Definition: C4Game.h:71
void SetMode(Mode eNewMode)
bool isLeague() const
C4Network2Res::Ref getNetRes() const
void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr)
Definition: C4Gui.h:1740
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:456
bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1096
#define C4GUI_ErrorFontClr
Definition: C4Gui.h:53
Sheet * AddSheet(const char *szTitle, int32_t icoTitle=Ico_None)
void OnBtnChat(C4GUI::Control *btn)
virtual Screen * GetScreen()
Definition: C4Gui.cpp:290
void OnCtxTabTeams(C4GUI::Element *pListItem)
Definition: C4GameLobby.h:120
void OnExitBtn(C4GUI::Control *btn)
virtual void CompileFunc(StdCompiler *pComp)
Definition: C4GameLobby.cpp:56
C4SHead Head
Definition: C4Scenario.h:230
const char * AtRelativePath(const char *szFilename)
Definition: C4Config.cpp:654
bool JoinLocalPlayer(const char *szLocalPlayerFilename, bool initial=false)
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
uint32_t DWORD
int32_t getPresentPercent() const
C4Application Application
Definition: C4Globals.cpp:44
bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1112
void SetClientSoundIcon(int32_t iForClientID)
const int32_t AlmostStartCountdownTime
Definition: C4GameLobby.h:27
void SetText(const char *szToText, bool fAllowHotkey=true)
Definition: C4GuiLabels.cpp:74
int32_t GetActiveSheetIndex()
virtual bool IsFocused(Control *pCtrl)
Definition: C4Gui.h:2113
void ClearText(bool fDoUpdate)
Definition: C4Gui.h:1744
void SetValue(const char *id, int32_t value, bool only_if_larger)
bool ProcessInput(const char *szText)
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277