OpenClonk
C4RoundResults.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2008-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 // Round result information to be displayed in game over dialog
17 
18 #include "C4Include.h"
19 #include "control/C4RoundResults.h"
20 
21 #include "object/C4Def.h"
22 #include "object/C4DefList.h"
23 #include "object/C4GameObjects.h"
24 #include "object/C4Object.h"
25 #include "player/C4Player.h"
26 #include "player/C4PlayerList.h"
27 
28 // *** C4RoundResultsPlayer
29 
31 {
32  // remember to adjust operator = and == when adding values here!
33  pComp->Value(mkNamingAdapt(id, "ID", 0));
34  // pComp->Value(mkNamingAdapt(fctBigIcon, "Icon", C4TargetFacet())); - not possible
35  pComp->Value(mkNamingAdapt(iTotalPlayingTime, "TotalPlayingTime", 0u));
36  pComp->Value(mkNamingAdapt(iScoreOld, "SettlementScoreOld", -1));
37  pComp->Value(mkNamingAdapt(iScoreNew, "SettlementScoreNew", -1));
38  pComp->Value(mkNamingAdapt(iLeagueScoreNew, "Score", -1)); // name used in league reply!
39  pComp->Value(mkNamingAdapt(iLeagueScoreGain, "GameScore", -1)); // name used in league reply!
40  pComp->Value(mkNamingAdapt(iLeagueRankNew, "Rank", 0)); // name used in league reply!
41  pComp->Value(mkNamingAdapt(iLeagueRankSymbolNew, "RankSymbol", 0)); // name used in league reply!
42  pComp->Value(mkNamingAdapt(sLeagueProgressData, "LeagueProgressData", StdCopyStrBuf()));
43  StdEnumEntry<LeagueStatus> LeagueStatusEntries[] =
44  {
45  { "", RRPLS_Unknown },
46  { "Lost", RRPLS_Lost },
47  { "Won", RRPLS_Won },
48  };
49  pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eLeagueStatus, LeagueStatusEntries), "Status", RRPLS_Unknown)); // name used in league reply!
50 }
51 
53 {
54  assert(pPlr);
55  // set fields by player
56  iTotalPlayingTime = pPlr->TotalPlayingTime;
57  if (pPlr->Evaluated)
58  {
59  iScoreNew = pPlr->TotalScore;
60  iScoreOld = iScoreNew - pPlr->LastRound.FinalScore;
61  }
62  else
63  {
64  // player not evaluated (e.g., removed by disconnect): Old score known only
65  iScoreOld = pPlr->TotalScore;
66  }
67  // load icon from player
68  fctBigIcon.Clear();
69  if (pPlr->BigIcon.Surface)
70  {
71  fctBigIcon.Create(pPlr->BigIcon.Wdt, pPlr->BigIcon.Hgt);
72  pPlr->BigIcon.Draw(fctBigIcon);
73  }
74  // progress data by player
75  C4PlayerInfo *pInfo = pPlr->GetInfo();
76  if (pInfo)
77  {
78  sLeagueProgressData.Copy(pInfo->GetLeagueProgressData());
79  }
80 }
81 
83 {
84  assert(pLeaguePlayerInfo);
85  //
86  // copy league info
87  iLeagueScoreNew = pLeaguePlayerInfo->iLeagueScoreNew;
88  iLeagueScoreGain = pLeaguePlayerInfo->iLeagueScoreGain;
89  iLeagueRankNew = pLeaguePlayerInfo->iLeagueRankNew;
90  iLeagueRankSymbolNew = pLeaguePlayerInfo->iLeagueRankSymbolNew;
91  sLeagueProgressData =pLeaguePlayerInfo->sLeagueProgressData;
92 }
93 
94 void C4RoundResultsPlayer::AddCustomEvaluationString(const char *szCustomString)
95 {
96  if (sCustomEvaluationStrings.getLength()) sCustomEvaluationStrings.Append(" ");
97  sCustomEvaluationStrings.Append(szCustomString);
98 }
99 
101 {
102  // cmp all xcept icon
103  if (id != cmp.id) return false;
104  if (iTotalPlayingTime != cmp.iTotalPlayingTime) return false;
105  if (iScoreOld != cmp.iScoreOld) return false;
106  if (iScoreNew != cmp.iScoreNew) return false;
107  if (sCustomEvaluationStrings != cmp.sCustomEvaluationStrings) return false;
108  if (iLeagueScoreNew != cmp.iLeagueScoreNew) return false;
109  if (iLeagueScoreGain != cmp.iLeagueScoreGain) return false;
110  if (iLeagueRankNew != cmp.iLeagueRankNew) return false;
111  if (iLeagueRankSymbolNew != cmp.iLeagueRankSymbolNew) return false;
112  if (eLeagueStatus != cmp.eLeagueStatus) return false;
113  if (sLeagueProgressData != cmp.sLeagueProgressData) return false;
114  return true;
115 }
116 
118 {
119  if (this == &cpy) return *this;
120  // assign all xcept icon
121  id = cpy.id;
122  iTotalPlayingTime = cpy.iTotalPlayingTime;
123  iScoreOld = cpy.iScoreOld;
124  iScoreNew = cpy.iScoreNew;
125  sCustomEvaluationStrings = cpy.sCustomEvaluationStrings;
126  iLeagueScoreNew = cpy.iLeagueScoreNew;
127  iLeagueScoreGain = cpy.iLeagueScoreGain;
128  iLeagueRankNew = cpy.iLeagueRankNew;
129  iLeagueRankSymbolNew = cpy.iLeagueRankSymbolNew;
130  eLeagueStatus = cpy.eLeagueStatus;
131  sLeagueProgressData = cpy.sLeagueProgressData;
132  return *this;
133 }
134 
135 
136 // *** C4RoundResultsPlayers
137 
139 {
140  while (iPlayerCount) delete ppPlayers[--iPlayerCount];
141  delete [] ppPlayers;
142  ppPlayers = nullptr;
143  iPlayerCapacity = 0;
144 }
145 
147 {
148  bool deserializing = pComp->isDeserializer();
149  if (deserializing) Clear();
150  int32_t iTemp = iPlayerCount;
151  pComp->Value(mkNamingCountAdapt<int32_t>(iTemp, "Player"));
152  if (iTemp < 0 || iTemp > C4MaxPlayer)
153  { pComp->excCorrupt("player count out of range"); return; }
154  // Grow list, if necessary
155  if (deserializing && iTemp > iPlayerCapacity)
156  {
157  GrowList(iTemp - iPlayerCapacity);
158  iPlayerCount = iTemp;
159  ZeroMem(ppPlayers, sizeof(*ppPlayers) * iPlayerCount);
160  }
161  // Compile
162  pComp->Value(mkNamingAdapt(mkArrayAdaptMap(ppPlayers, iPlayerCount, mkPtrAdaptNoNull<C4RoundResultsPlayer>), "Player"));
163  // Force specialization
164  mkPtrAdaptNoNull<C4RoundResultsPlayer>(*ppPlayers);
165 }
166 
168 {
169  if (idx>=0 && idx<iPlayerCount)
170  return ppPlayers[idx];
171  else
172  return nullptr;
173 }
174 
176 {
177  for (int32_t idx=0; idx<iPlayerCount; ++idx)
178  if (ppPlayers[idx]->GetID() == id)
179  return ppPlayers[idx];
180  return nullptr;
181 }
182 
183 void C4RoundResultsPlayers::GrowList(size_t iByVal)
184 {
185  // create new list (out of mem: simply returns here; info list remains in a valid state)
186  C4RoundResultsPlayer **ppNew = new C4RoundResultsPlayer *[iPlayerCapacity += iByVal];
187  // move existing
188  if (ppPlayers)
189  {
190  memcpy(ppNew, ppPlayers, iPlayerCount * sizeof(C4RoundResultsPlayer *));
191  delete [] ppPlayers;
192  }
193  // assign new
194  ppPlayers = ppNew;
195 }
196 
198 {
199  assert(pNewPlayer);
200  if (iPlayerCount == iPlayerCapacity) GrowList(4);
201  ppPlayers[iPlayerCount++] = pNewPlayer;
202 }
203 
205 {
206  assert(id);
207  // find existing
208  C4RoundResultsPlayer *pPlr = GetByID(id);
209  // not found: Add new
210  if (!pPlr)
211  {
212  pPlr = new C4RoundResultsPlayer();
213  pPlr->SetID(id);
214  Add(pPlr);
215  }
216  return pPlr;
217 }
218 
220 {
221  if (iPlayerCount != cmp.iPlayerCount) return false;
222  for (int32_t i = 0; i < iPlayerCount; ++i)
223  if (!(*ppPlayers[i] == *cmp.ppPlayers[i]))
224  return false;
225  // equal
226  return true;
227 }
228 
230 {
231  Clear();
232  C4RoundResultsPlayer *pPlr; int32_t i=0;
233  while ((pPlr = cpy.GetByIndex(i++)))
234  Add(new C4RoundResultsPlayer(*pPlr));
235  return *this;
236 }
237 
238 
239 // *** C4RoundResults
240 
242 {
243  Players.Clear();
244  Goals.Clear();
245  iPlayingTime = 0;
246  sCustomEvaluationStrings.Clear();
247  iLeaguePerformance = 0;
248  sNetResult.Clear();
249  eNetResult = NR_None;
250  fHideSettlementScore=false;
251  Statistics.Clear();
252 }
253 
255 {
256  if (Game.C4S.Game.IsMelee())
257  fHideSettlementScore=true;
258  else fHideSettlementScore=false;
259 }
260 
262 {
263  bool deserializing = pComp->isDeserializer();
264  if (deserializing) Clear();
265  pComp->Value(mkNamingAdapt(Goals, "Goals", C4IDList()));
266  pComp->Value(mkNamingAdapt(iPlayingTime, "PlayingTime", 0u));
267  pComp->Value(mkNamingAdapt(fHideSettlementScore, "HideSettlementScore", Game.C4S.Game.IsMelee()));
268  pComp->Value(mkNamingAdapt(sCustomEvaluationStrings, "CustomEvaluationStrings", StdCopyStrBuf()));
269  pComp->Value(mkNamingAdapt(iLeaguePerformance, "LeaguePerformance", 0));
270  pComp->Value(mkNamingAdapt(Players, "PlayerInfos", C4RoundResultsPlayers()));
271  pComp->Value(mkNamingAdapt(sNetResult, "NetResult", StdCopyStrBuf()));
272  StdEnumEntry<NetResult> NetResultEntries[] =
273  {
274  { "", NR_None },
275  { "LeagueOK", NR_LeagueOK },
276  { "LeagueError", NR_LeagueError},
277  { "NetError", NR_NetError },
278  };
279  pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eNetResult, NetResultEntries), "NetResult", NR_None));
280 }
281 
282 void C4RoundResults::EvaluateGoals(C4IDList &GoalList, C4IDList &FulfilledGoalList, int32_t iPlayerNumber)
283 {
284  // clear prev
285  GoalList.Clear(); FulfilledGoalList.Clear();
286  // Items
287  int32_t cnt; C4ID idGoal;
288  for (cnt=0; (idGoal=::Objects.GetListID(C4D_Goal,cnt)); cnt++)
289  {
290  // determine if the goal is fulfilled - do the calls even if the menu is not to be opened to ensure synchronization
291  bool fFulfilled = false;;
292  C4Object *pObj = C4Id2Def(idGoal) ? ::Objects.Find(::Definitions.ID2Def(idGoal)) : nullptr;
293  if (pObj)
294  {
295  // Check fulfilled per player, this enables the possibility of rivalry.
296  C4AulParSet pars(iPlayerNumber);
297  fFulfilled = !!pObj->Call(PSF_IsFulfilled, &pars);
298  }
299  GoalList.SetIDCount(idGoal, cnt, true);
300  if (fFulfilled) FulfilledGoalList.SetIDCount(idGoal, 1, true);
301  }
302 }
303 
305 {
306  // set game data
307  C4Player *pFirstLocalPlayer = ::Players.GetLocalByIndex(0);
308  int32_t iFirstLocalPlayer = pFirstLocalPlayer ? pFirstLocalPlayer->Number : NO_OWNER;
309  EvaluateGoals(Goals, FulfilledGoals, iFirstLocalPlayer);
310  iPlayingTime = Game.Time;
311 
312  // collect statistics
313  try
314  {
315  C4AulParSet Pars;
316  auto stats = ::ScriptEngine.GetPropList()->Call(PSF_CollectStatistics, &Pars);
317  if (stats != C4VNull)
318  Statistics = stats.ToJSON();
319  }
320  catch (C4JSONSerializationError& e)
321  {
322  DebugLogF("ERROR: cannot serialize CollectStatistics() result: %s", e.what());
323  }
324 }
325 
326 void C4RoundResults::EvaluateNetwork(C4RoundResults::NetResult eNetResult, const char *szResultMsg)
327 {
328  // take result only if there was no previous result (the previous one is usually more specific)
329  if (!HasNetResult())
330  {
331  this->eNetResult = eNetResult;
332  if (szResultMsg) sNetResult.Copy(szResultMsg); else sNetResult.Clear();
333  }
334 }
335 
336 void C4RoundResults::EvaluateLeague(const char *szResultMsg, bool fSuccess, const C4RoundResultsPlayers &rLeagueInfo)
337 {
338  // League evaluation imples network evaluation
340  // Evaluation called by league: Sets new league scores and ranks
341  C4RoundResultsPlayer *pPlr, *pOwnPlr; int32_t i = 0;
342  while ((pPlr = rLeagueInfo.GetByIndex(i++)))
343  {
344  pOwnPlr = Players.GetCreateByID(pPlr->GetID());
345  pOwnPlr->EvaluateLeague(pPlr);
346  }
347 }
348 
350 {
351  // Evaluation called by player when it's evaluated
352  assert(pPlr);
353  C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(pPlr->ID);
354  pOwnPlr->EvaluatePlayer(pPlr);
355 }
356 
357 void C4RoundResults::AddCustomEvaluationString(const char *szCustomString, int32_t idPlayer)
358 {
359  // Set custom string to be shown in game over dialog
360  // idPlayer==0 for global strings
361  if (!idPlayer)
362  {
363  if (sCustomEvaluationStrings.getLength()) sCustomEvaluationStrings.AppendChar('|');
364  sCustomEvaluationStrings.Append(szCustomString);
365  }
366  else
367  {
368  C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(idPlayer);
369  pOwnPlr->AddCustomEvaluationString(szCustomString);
370  }
371 }
372 
374 {
375  fHideSettlementScore=fHide;
376 }
377 
379 {
380  return fHideSettlementScore;
381 }
382 
383 void C4RoundResults::SetLeaguePerformance(int32_t iNewPerf, int32_t idPlayer)
384 {
385  // Store to be sent later. idPlayer == 0 means global performance.
386  if(!idPlayer)
387  {
388  iLeaguePerformance = iNewPerf;
389  }
390  else
391  {
392  C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(idPlayer);
393  pOwnPlr->SetLeaguePerformance(iNewPerf);
394  }
395  }
396 
397 int32_t C4RoundResults::GetLeaguePerformance(int32_t idPlayer) const
398 {
399  if(!idPlayer)
400  return iLeaguePerformance;
401  else
402  if(C4RoundResultsPlayer *pPlr = Players.GetByID(idPlayer))
403  return pPlr->GetLeaguePerformance();
404  return 0;
405 }
406 
407 bool C4RoundResults::Load(C4Group &hGroup, const char *szFilename)
408 {
409  // clear previous
410  Clear();
411  // load file contents
412  StdStrBuf Buf;
413  if (!hGroup.LoadEntryString(szFilename, &Buf)) return false;
414  // compile
415  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "RoundResults"), Buf, szFilename)) return false;
416  // done, success
417  return true;
418 }
419 
420 bool C4RoundResults::Save(C4Group &hGroup, const char *szFilename)
421 {
422  // remove previous entry from group
423  hGroup.DeleteEntry(szFilename);
424  // decompile
425  try
426  {
427  StdStrBuf Buf = DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(*this, "RoundResults"));
428  // save it, if not empty
429  if (Buf.getLength())
430  if (!hGroup.Add(szFilename, Buf, false, true))
431  return false;
432  }
433  catch (StdCompiler::Exception *)
434  { return false; }
435  // done, success
436  return true;
437 }
438 
439 
440 // *** C4PacketLeagueRoundResults
441 
443 {
444  pComp->Value(mkNamingAdapt(fSuccess, "Success", false));
445  pComp->Value(mkNamingAdapt(sResultsString, "ResultString", StdCopyStrBuf()));
446  pComp->Value(Players);
447 }
448 
const int NO_OWNER
Definition: C4Constants.h:137
const int32_t C4D_Goal
Definition: C4Def.h:46
C4Def * C4Id2Def(C4ID id)
Definition: C4DefList.h:84
#define PSF_IsFulfilled
Definition: C4GameScript.h:84
#define PSF_CollectStatistics
Definition: C4GameScript.h:105
C4Game Game
Definition: C4Globals.cpp:52
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
C4GameObjects Objects
Definition: C4Globals.cpp:48
C4DefList Definitions
Definition: C4Globals.cpp:49
bool DebugLogF(const char *strMessage ...)
Definition: C4Log.cpp:290
const int32_t C4MaxPlayer
Definition: C4Player.h:37
const C4Value C4VNull
Definition: C4Value.cpp:30
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:60
StdArrayAdapt< T, M > mkArrayAdaptMap(T *pArray, int iSize, M &&map)
Definition: StdAdaptors.h:340
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
Definition: StdAdaptors.h:795
C4PropListStatic * GetPropList()
Definition: C4Aul.h:151
C4Def * ID2Def(C4ID id)
C4Surface * Surface
Definition: C4Facet.h:117
float Hgt
Definition: C4Facet.h:118
float Wdt
Definition: C4Facet.h:118
void Draw(C4Facet &cgo, bool fAspect=true, int32_t iPhaseX=0, int32_t iPhaseY=0, bool fTransparent=true)
Definition: C4Facet.cpp:154
bool Create(int iWdt, int iHgt, int iWdt2=C4FCT_Full, int iHgt2=C4FCT_Full)
Definition: C4FacetEx.cpp:54
void Clear()
Definition: C4FacetEx.h:44
C4RoundResults & RoundResults
Definition: C4Game.h:73
C4Scenario C4S
Definition: C4Game.h:74
int32_t Time
Definition: C4Game.h:132
bool DeleteEntry(const char *filename, bool do_recycle=false)
Definition: C4Group.cpp:1695
bool Add(const char *filename, const char *entry_name)
Definition: C4Group.cpp:1621
bool LoadEntryString(const char *entry_name, StdStrBuf *buffer)
Definition: C4Group.cpp:2430
Definition: C4Id.h:26
bool SetIDCount(C4ID c_id, int32_t iCount, bool fAddNewID=false)
Definition: stub-handle.cpp:68
void Clear()
Definition: stub-handle.cpp:64
const char * what() const noexcept override
Definition: C4Value.h:69
C4ID GetListID(int32_t dwCategory, int index) const
C4Object * Find(C4Def *def, int owner=ANY_OWNER, DWORD dwOCF=OCF_All)
void CompileFunc(StdCompiler *pComp) override
C4RoundResultsPlayers Players
int32_t ID
Definition: C4Player.h:87
C4FacetSurface BigIcon
Definition: C4Player.h:138
bool Evaluated
Definition: C4Player.h:85
int32_t Number
Definition: C4Player.h:86
class C4PlayerInfo * GetInfo()
Definition: C4Player.cpp:1552
int32_t TotalPlayingTime
Definition: C4InfoCore.h:91
C4RoundResult LastRound
Definition: C4InfoCore.h:92
int32_t TotalScore
Definition: C4InfoCore.h:89
const char * GetLeagueProgressData() const
Definition: C4PlayerInfo.h:192
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4PropList.h:114
int32_t FinalScore
Definition: C4InfoCore.h:71
void EvaluatePlayer(C4Player *pPlr)
void EvaluateLeague(const char *szResultMsg, bool fSuccess, const C4RoundResultsPlayers &rLeagueInfo)
bool Save(C4Group &hGroup, const char *szFilename=C4CFN_RoundResults)
void EvaluateNetwork(NetResult eResult, const char *szResultsString)
void CompileFunc(StdCompiler *pComp)
bool Load(C4Group &hGroup, const char *szFilename=C4CFN_RoundResults)
bool HasNetResult() const
static void EvaluateGoals(C4IDList &GoalList, C4IDList &FulfilledGoalList, int32_t iPlayerNumber)
void HideSettlementScore(bool fHide=true)
void AddCustomEvaluationString(const char *szCustomString, int32_t idPlayer)
void SetLeaguePerformance(int32_t iNewPerf, int32_t idPlayer=0)
int32_t GetLeaguePerformance(int32_t idPlayer=0) const
bool SettlementScoreIsHidden()
void SetLeaguePerformance(int32_t iNewPerf)
void EvaluatePlayer(C4Player *pPlr)
bool operator==(const C4RoundResultsPlayer &cmp)
void AddCustomEvaluationString(const char *szCustomString)
int32_t GetID() const
C4RoundResultsPlayer & operator=(const C4RoundResultsPlayer &cpy)
void EvaluateLeague(C4RoundResultsPlayer *pLeaguePlayer)
void SetID(int32_t idNew)
void CompileFunc(StdCompiler *pComp)
C4RoundResultsPlayer * GetCreateByID(int32_t id)
C4RoundResultsPlayers & operator=(const C4RoundResultsPlayers &cpy)
bool operator==(const C4RoundResultsPlayers &cmp)
C4RoundResultsPlayer * GetByID(int32_t id) const
void Add(C4RoundResultsPlayer *pNewPlayer)
C4RoundResultsPlayer * GetByIndex(int32_t idx) const
void CompileFunc(StdCompiler *pComp)
bool IsMelee()
Definition: C4Scenario.cpp:512
C4SGame Game
Definition: C4Scenario.h:234
void excCorrupt(const char *szMessage,...)
Definition: StdCompiler.h:249
void Value(const T &rStruct)
Definition: StdCompiler.h:161
virtual bool isDeserializer()
Definition: StdCompiler.h:53
void AppendChar(char cChar)
Definition: StdBuf.h:588
void Copy()
Definition: StdBuf.h:467
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
void Clear()
Definition: StdBuf.h:466
size_t getLength() const
Definition: StdBuf.h:445