OpenClonk
C4Scoreboard.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2005-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 // script-controlled InGame dialog to show player infos
17 
18 #include "C4Include.h"
19 #include "gui/C4Scoreboard.h"
20 
21 #include "gui/C4GameOverDlg.h"
22 #include "gui/C4Gui.h"
23 #include "graphics/C4Draw.h"
25 
26 
28 {
29 private:
30  int32_t *piColWidths;
31  C4Scoreboard *pBrd;
32 
33  enum { XIndent = 4, YIndent = 4, XMargin = 3, YMargin = 3 };
34 
35 public:
36  C4ScoreboardDlg(C4Scoreboard *pForScoreboard);
37  ~C4ScoreboardDlg() override;
38 
39 protected:
40  void InvalidateRows() { delete [] piColWidths; piColWidths = nullptr; }
41  void Update(); // update row widths and own size and caption
42 
43  bool DoPlacement(C4GUI::Screen *pOnScreen, const C4Rect &rPreferredDlgRect) override;
44  void Draw(C4TargetFacet &cgo) override;
45  void DrawElement(C4TargetFacet &cgo) override;
46 
47  const char *GetID() override { return "Scoreboard"; }
48 
49  bool IsMouseControlled() override { return false; }
50 
51  friend class C4Scoreboard;
52 };
53 
54 // ************************************************
55 // *** C4Scoreboard
56 
57 void C4Scoreboard::Entry::SwapWith(Entry *pSwap)
58 {
59  Entry swp; swp.Text.Take(std::move(Text)); swp.iVal = iVal;
60  Text.Take(std::move(pSwap->Text)); iVal = pSwap->iVal;
61  pSwap->Text.Take(std::move(swp.Text)); pSwap->iVal = swp.iVal;
62 }
63 
65 {
66  // del all cells
67  delete [] pEntries;
68  pEntries = nullptr; iRows=iCols=0;
69  // del dialog
70  iDlgShow = 0;
71  if (pDlg) { delete pDlg; pDlg = nullptr; }
72 }
73 
74 void C4Scoreboard::AddRow(int32_t iInsertBefore)
75 {
76  // counts
77  int32_t iNewEntryCount = (iRows+1) * iCols;
78  if (!iNewEntryCount) { ++iRows; return; }
79  // realloc and copy array
80  Entry *pNewEntries = new Entry[iNewEntryCount];
81  for (int32_t iRow = 0, iFromRow = 0; iFromRow < iRows; ++iRow,++iFromRow)
82  {
83  if (iFromRow == iInsertBefore) ++iRow;
84  for (int32_t iCol = 0; iCol < iCols; ++iCol)
85  pNewEntries[iRow*iCols+iCol].GrabFrom(GetCell(iCol, iFromRow));
86  }
87  ++iRows;
88  delete [] pEntries; pEntries = pNewEntries;
89 }
90 
91 void C4Scoreboard::AddCol(int32_t iInsertBefore)
92 {
93  // counts
94  int32_t iNewEntryCount = iRows * (iCols + 1);
95  if (!iNewEntryCount) { ++iCols; return; }
96  // realloc and copy array
97  Entry *pNewEntries = new Entry[iNewEntryCount];
98  for (int32_t iRow = 0; iRow < iRows; ++iRow)
99  for (int32_t iCol = 0, iFromCol = 0; iFromCol < iCols; ++iCol, ++iFromCol)
100  {
101  if (iFromCol == iInsertBefore) ++iCol;
102  pNewEntries[iRow*(iCols+1)+iCol].GrabFrom(GetCell(iFromCol, iRow));
103  }
104  ++iCols; // CAUTION: must inc after add, so GetCell won't point into bogus!
105  delete [] pEntries; pEntries = pNewEntries;
106 }
107 
108 void C4Scoreboard::DelRow(int32_t iDelIndex)
109 {
110  // counts
111  int32_t iNewEntryCount = (iRows-1) * iCols;
112  if (!iNewEntryCount) { --iRows; delete [] pEntries; pEntries = nullptr; return; }
113  // realloc and copy array
114  Entry *pNewEntries = new Entry[iNewEntryCount]; Entry *pCpy = pNewEntries;
115  for (int32_t iRow = 0, iFromRow = 0; iRow < (iRows-1); ++iRow,++iFromRow)
116  {
117  if (iFromRow == iDelIndex) ++iFromRow;
118  for (int32_t iCol = 0; iCol < iCols; ++iCol)
119  pCpy++->GrabFrom(GetCell(iCol, iFromRow));
120  }
121  --iRows;
122  delete [] pEntries; pEntries = pNewEntries;
123 }
124 
125 void C4Scoreboard::DelCol(int32_t iDelIndex)
126 {
127  // counts
128  int32_t iNewEntryCount = iRows * (iCols-1);
129  if (!iNewEntryCount) { --iCols; delete [] pEntries; pEntries = nullptr; return; }
130  // realloc and copy array
131  Entry *pNewEntries = new Entry[iNewEntryCount]; Entry *pCpy = pNewEntries;
132  for (int32_t iRow = 0; iRow < iRows; ++iRow)
133  for (int32_t iCol = 0, iFromCol = 0; iCol < (iCols-1); ++iCol,++iFromCol)
134  {
135  if (iFromCol == iDelIndex) ++iFromCol;
136  pCpy++->GrabFrom(GetCell(iFromCol, iRow));
137  }
138  --iCols; // CAUTION: must dec after add, so GetCell won't point into bogus!
139  delete [] pEntries; pEntries = pNewEntries;
140 }
141 
142 void C4Scoreboard::SwapRows(int32_t iRow1, int32_t iRow2)
143 {
144  Entry *pXChg1 = pEntries+iRow1*iCols;
145  Entry *pXChg2 = pEntries+iRow2*iCols;
146  int32_t i = iCols; while (i--) pXChg1++->SwapWith(pXChg2++);
147 }
148 
149 int32_t C4Scoreboard::GetColByKey(int32_t iKey) const
150 {
151  // safety
152  if (!iRows) return -1;
153  // check all col headers
154  Entry *pCheck = pEntries;
155  for (int32_t i = 0; i < iCols; ++i)
156  if (pCheck++->iVal == iKey) return i;
157  return -1;
158 }
159 
160 int32_t C4Scoreboard::GetRowByKey(int32_t iKey) const
161 {
162  // safety
163  if (!iCols) return -1;
164  // check all row headers
165  Entry *pCheck = pEntries;
166  for (int32_t i = 0; i < iRows; ++i,(pCheck+=iCols))
167  if (pCheck->iVal == iKey) return i;
168  return -1;
169 }
170 
171 void C4Scoreboard::SetCell(int32_t iColKey, int32_t iRowKey, const char *szValue, int32_t iValue)
172 {
173  // ensure primary row/col exists
174  if (!iCols || !iRows)
175  {
176  if (!iCols) AddCol(0);
177  if (!iRows) AddRow(0);
178  GetCell(0, 0)->iVal = TitleKey;
179  }
180  // get row/col; create new if not yet existing
181  int32_t iCol = GetColByKey(iColKey);
182  if (iCol<0) { AddCol(iCol=iCols); GetCell(iCol, 0)->iVal = iColKey; }
183  int32_t iRow = GetRowByKey(iRowKey);
184  if (iRow<0) { AddRow(iRow=iRows); GetCell(0, iRow)->iVal = iRowKey; }
185  // now set values
186  Entry *pCell = GetCell(iCol, iRow);
187  pCell->Text.Copy(szValue);
188  if (iCol && iRow) pCell->iVal = iValue; // do NOT overwrite index keys!
189  // if an empty value was set, prune empty
190  if (!szValue || !*szValue)
191  {
192  // prune empty row (not label row)
193  int32_t i;
194  if (iRow)
195  {
196  for (i=1; i<iCols; ++i) if (GetCell(i, iRow)->Text) break;
197  if (i == iCols) DelRow(iRow);
198  }
199  // prune empty col (not label col)
200  if (iCol)
201  {
202  for (i=1; i<iRows; ++i) if (GetCell(iCol, i)->Text) break;
203  if (i == iRows) DelCol(iCol);
204  }
205  }
206  /* // prune empty board? but this would prevent boards that just sort
207  if (iRows == 1 && iCols == 1)
208  Clear(); // must not do this, because it will del pDlg
209  else*/
210  // recalc row widths in display (else done by clear)
211  InvalidateRows();
212 }
213 
214 const char *C4Scoreboard::GetCellString(int32_t iColKey, int32_t iRowKey)
215 {
216  // get row/col
217  int32_t iCol = GetColByKey(iColKey);
218  int32_t iRow = GetRowByKey(iRowKey);
219  if (iCol<0 || iRow<0) return nullptr;
220  // now get value
221  Entry *pCell = GetCell(iCol, iRow);
222  return pCell->Text.getData();
223 }
224 
225 int32_t C4Scoreboard::GetCellData(int32_t iColKey, int32_t iRowKey)
226 {
227  // get row/col
228  int32_t iCol = GetColByKey(iColKey);
229  int32_t iRow = GetRowByKey(iRowKey);
230  if (iCol<0 || iRow<0) return 0;
231  // now get value
232  Entry *pCell = GetCell(iCol, iRow);
233  return pCell->iVal;
234 }
235 
236 void C4Scoreboard::RemoveCol(int32_t iColKey)
237 {
238  int32_t iCol = GetColByKey(iColKey);
239  if (iCol>=0) DelCol(iCol);
240  InvalidateRows(); // recalc row widths in display
241 }
242 
243 void C4Scoreboard::RemoveRow(int32_t iRowKey)
244 {
245  int32_t iRow = GetRowByKey(iRowKey);
246  if (iRow>=0) DelRow(iRow);
247  InvalidateRows(); // recalc row widths in display
248 }
249 
250 bool C4Scoreboard::SortBy(int32_t iColKey, bool fReverse)
251 {
252  // get sort col
253  int32_t iCol = GetColByKey(iColKey);
254  if (iCol<0) return false;
255  // sort
256  int32_t iSortDir = fReverse ? -1 : +1;
257  int32_t iSortBegin=1, iSortEnd=iRows-1;
258  while (iSortBegin < iSortEnd)
259  {
260  int32_t iNewBorder = iSortBegin; int32_t i;
261  for (i = iSortBegin; i < iSortEnd; ++i)
262  if (GetCell(iCol, i)->iVal * iSortDir > GetCell(iCol, i+1)->iVal * iSortDir)
263  {
264  SwapRows(i, i+1);
265  iNewBorder = i;
266  }
267  iSortEnd = iNewBorder;
268  for (i = iSortEnd; i > iSortBegin; --i)
269  if (GetCell(iCol, i-1)->iVal * iSortDir > GetCell(iCol, i)->iVal * iSortDir)
270  {
271  SwapRows(i-1, i);
272  iNewBorder = i;
273  }
274  iSortBegin = iNewBorder;
275  }
276  return true;
277 }
278 
279 void C4Scoreboard::InvalidateRows()
280 {
281  // recalculate row sizes
282  if (pDlg) pDlg->InvalidateRows();
283 }
284 
285 void C4Scoreboard::DoDlgShow(int32_t iChange, bool fUserToggle)
286 {
287  if (::pGUI->IsExclusive()) return;
288  // update dlg show
289  iDlgShow += iChange;
290  if (!fUserToggle)
291  // script update: Dlg on off if iDlgShow variable passed zero
292  fUserToggle = (ShouldBeShown() == !pDlg);
293  else
294  // user pressed Tab: Always toggle except if the scoreboard cannot be shown at all
295  if (!CanBeShown() && !pDlg) fUserToggle = false;
296  if (fUserToggle)
297  {
298  if (!pDlg)
299  {
300  if (!C4GameOverDlg::IsShown()) // never show during game over dlg
301  ::pGUI->ShowRemoveDlg(pDlg = new C4ScoreboardDlg(this));
302  }
303  else
304  pDlg->Close(false);
305  }
306 }
307 
309 {
310  if (::pGUI->IsExclusive()) return;
311  // hide scoreboard if it was active
312  if (pDlg) pDlg->Close(false);
313 }
314 
316 {
317  bool deserializing = pComp->isDeserializer();
318  if (deserializing) Clear();
319  pComp->Value(mkNamingAdapt(iRows, "Rows", 0));
320  pComp->Value(mkNamingAdapt(iCols, "Cols", 0));
321  pComp->Value(mkNamingAdapt(iDlgShow, "DlgShow", 0));
322  if (iRows * iCols)
323  {
324  if (deserializing) pEntries = new Entry[iRows * iCols];
325  for (int32_t iRow = 0; iRow < iRows; ++iRow)
326  for (int32_t iCol = 0; iCol < iCols; ++iCol)
327  {
328  Entry *pEnt = GetCell(iCol, iRow);
329  pComp->Value(mkNamingAdapt(pEnt->Text, FormatString("Cell%i_%iString", iCol, iRow).getData()));
330  pComp->Value(mkNamingAdapt(pEnt->iVal, FormatString("Cell%i_%iValue", iCol, iRow).getData()));
331  }
332  // recheck dlg show in read mode
333  // will usually not do anything, because reading is done before enetering shared mode
334  if (pComp->isDeserializer()) DoDlgShow(0, false);
335  }
336 }
337 
338 
339 // ************************************************
340 // *** C4ScoreboardDlg
341 
343  : C4GUI::Dialog(100, 100, "nops", false), piColWidths(nullptr), pBrd(pForScoreboard)
344 {
345  Update();
346 }
347 
349 {
350  delete [] piColWidths;
351  pBrd->pDlg = nullptr;
352 }
353 
355 {
356  // counts
357  int32_t iRowCount = pBrd->iRows; int32_t iColCount = pBrd->iCols;
358  delete [] piColWidths; piColWidths = nullptr;
359  // invalid board - scipters can create those, but there's no reason why the engine
360  // should display something pretty then; just keep dialog defaults
361  if (!iRowCount || !iColCount) return;
362  // calc sizes as max col widths plus some indent pixels
363  piColWidths = new int32_t[iColCount];
364  int32_t iWdt=XMargin*2, iHgt=YMargin*2;
365  for (int32_t iCol = 0; iCol < iColCount; ++iCol)
366  {
367  piColWidths[iCol] = XIndent;
368  for (int32_t iRow = 0; iRow < iRowCount; ++iRow)
369  {
370  C4Scoreboard::Entry *pCell = pBrd->GetCell(iCol, iRow);
371  if ((iRow || iCol) && !pCell->Text.isNull()) piColWidths[iCol] = std::max<int32_t>(piColWidths[iCol], ::GraphicsResource.FontRegular.GetTextWidth(pCell->Text.getData()) + XIndent);
372  }
373  iWdt += piColWidths[iCol];
374  }
375  iHgt += iRowCount * (::GraphicsResource.FontRegular.GetLineHeight() + YIndent);
376  const char *szTitle = pBrd->GetCell(0,0)->Text.getData();
377  if (szTitle) iWdt = std::max<int32_t>(iWdt, ::GraphicsResource.FontRegular.GetTextWidth(szTitle) + 40);
378  if (!pTitle != !szTitle) SetTitle(szTitle); // needed for title margin...
379  iWdt += GetMarginLeft() + GetMarginRight();
380  iHgt += GetMarginTop() + GetMarginBottom();
381  // update dialog
382  SetBounds(C4Rect(rcBounds.x, rcBounds.y, iWdt, iHgt));
383  SetTitle(szTitle);
385  // realign
386  C4GUI::Screen *pScr = GetScreen();
387  if (pScr) DoPlacement(pScr, pScr->GetPreferredDlgRect());
388 }
389 
390 bool C4ScoreboardDlg::DoPlacement(C4GUI::Screen *pOnScreen, const C4Rect &rPreferredDlgRect)
391 {
392  // align topright
393  SetPos(rPreferredDlgRect.x + rPreferredDlgRect.Wdt - rcBounds.Wdt - 20, rPreferredDlgRect.y + 38);
394  return true;
395 }
396 
398 {
399  if (!piColWidths) Update();
400  typedef C4GUI::Dialog ParentClass;
401  ParentClass::Draw(cgo);
402 }
403 
405 {
406  typedef C4GUI::Dialog ParentClass;
407  ParentClass::DrawElement(cgo);
408  // draw spreadsheet
409  int32_t iRowCount = pBrd->iRows; int32_t iColCount = pBrd->iCols;
410  int32_t iY = YMargin + int32_t(cgo.TargetY) + rcClientRect.y;
411  for (int32_t iRow = 0; iRow < iRowCount; ++iRow)
412  {
413  int32_t iX = XMargin + int32_t(cgo.TargetX) + rcClientRect.x;
414  for (int32_t iCol = 0; iCol < iColCount; ++iCol)
415  {
416  const char *szText = pBrd->GetCell(iCol, iRow)->Text.getData();
417  if (szText && *szText && (iRow || iCol))
418  pDraw->TextOut(szText, ::GraphicsResource.FontRegular, 1.0, cgo.Surface, iCol ? iX + piColWidths[iCol]/2 : iX, iY, 0xffffffff, iCol ? ACenter : ALeft);
419  iX += piColWidths[iCol];
420  }
421  iY += ::GraphicsResource.FontRegular.GetLineHeight() + YIndent;
422  }
423 }
C4Draw * pDraw
Definition: C4Draw.cpp:42
C4GraphicsResource GraphicsResource
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
const int ALeft
Definition: C4Surface.h:41
const int ACenter
Definition: C4Surface.h:41
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
bool TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, float iTx, float iTy, DWORD dwFCol=0xffffffff, BYTE byForm=ALeft, bool fDoMarkup=true)
Definition: C4Draw.cpp:561
C4Surface * Surface
Definition: C4Facet.h:117
int32_t GetMarginTop() override
Definition: C4Gui.h:2141
int32_t GetMarginBottom() override
Definition: C4Gui.h:2144
void Close(bool fOK)
int32_t GetMarginRight() override
Definition: C4Gui.h:2143
int32_t GetMarginLeft() override
Definition: C4Gui.h:2142
WoodenLabel * pTitle
Definition: C4Gui.h:2079
void SetTitle(const char *szToTitle, bool fShowCloseButton=true)
C4Rect rcBounds
Definition: C4Gui.h:385
virtual Screen * GetScreen()
Definition: C4Gui.cpp:289
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
static C4Facet GetIconFacet(Icons icoIconIndex)
bool ShowRemoveDlg(Dialog *pDlg)
bool IsExclusive()
Definition: C4Gui.h:2670
const C4Rect & GetPreferredDlgRect()
Definition: C4Gui.h:2603
void SetPos(int32_t iXPos, int32_t iYPos)
Definition: C4Gui.h:860
C4Rect rcClientRect
Definition: C4Gui.h:851
void SetIcon(const C4Facet &rfctIcon)
static bool IsShown()
Definition: C4GameOverDlg.h:91
Definition: C4Rect.h:28
int32_t y
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
int32_t x
Definition: C4Rect.h:30
C4ScoreboardDlg(C4Scoreboard *pForScoreboard)
void DrawElement(C4TargetFacet &cgo) override
const char * GetID() override
bool DoPlacement(C4GUI::Screen *pOnScreen, const C4Rect &rPreferredDlgRect) override
void Draw(C4TargetFacet &cgo) override
~C4ScoreboardDlg() override
bool IsMouseControlled() override
bool CanBeShown()
Definition: C4Scoreboard.h:84
int32_t iDlgShow
Definition: C4Scoreboard.h:61
class C4ScoreboardDlg * pDlg
Definition: C4Scoreboard.h:60
bool SortBy(int32_t iColKey, bool fReverse)
bool ShouldBeShown()
Definition: C4Scoreboard.h:83
void DoDlgShow(int32_t iChange, bool fUserToggle)
void CompileFunc(StdCompiler *pComp)
Entry * GetCell(int32_t iCol, int32_t iRow) const
Definition: C4Scoreboard.h:64
int32_t GetCellData(int32_t iColKey, int32_t iRowKey)
void RemoveCol(int32_t iColKey)
void RemoveRow(int32_t iRowKey)
void SetCell(int32_t iColKey, int32_t iRowKey, const char *szValue, int32_t iValue)
const char * GetCellString(int32_t iColKey, int32_t iRowKey)
float TargetY
Definition: C4Facet.h:165
float TargetX
Definition: C4Facet.h:165
int GetLineHeight() const
Definition: C4FontLoader.h:125
int32_t GetTextWidth(const char *szText, bool fCheckMarkup=true)
Definition: C4FontLoader.h:140
void Value(const T &rStruct)
Definition: StdCompiler.h:161
virtual bool isDeserializer()
Definition: StdCompiler.h:53
@ Ico_Player
Definition: C4Gui.h:650