| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2008, Sven2 |
| 6 | * Copyright (c) 2017-2022, 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 | // Round result information to be displayed in game over dialog |
| 19 | |
| 20 | #include <C4Include.h> |
| 21 | #include <C4RoundResults.h> |
| 22 | |
| 23 | #include <C4Player.h> |
| 24 | #include <C4GraphicsSystem.h> |
| 25 | #include <C4Game.h> |
| 26 | #include <C4Object.h> |
| 27 | #include <C4Wrappers.h> |
| 28 | |
| 29 | // C4RoundResultsPlayer |
| 30 | |
| 31 | void C4RoundResultsPlayer::CompileFunc(StdCompiler *pComp) |
| 32 | { |
| 33 | // remember to adjust operator = and == when adding values here! |
| 34 | pComp->Value(rStruct: mkNamingAdapt(rValue&: id, szName: "ID" , rDefault: 0)); |
| 35 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iTotalPlayingTime, szName: "TotalPlayingTime" , rDefault: 0u)); |
| 36 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iScoreOld, szName: "SettlementScoreOld" , rDefault: -1)); |
| 37 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iScoreNew, szName: "SettlementScoreNew" , rDefault: -1)); |
| 38 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iLeagueScoreNew, szName: "Score" , rDefault: -1)); // name used in league reply! |
| 39 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iLeagueScoreGain, szName: "GameScore" , rDefault: -1)); // name used in league reply! |
| 40 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iLeagueRankNew, szName: "Rank" , rDefault: 0)); // name used in league reply! |
| 41 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iLeagueRankSymbolNew, szName: "RankSymbol" , rDefault: 0)); // name used in league reply! |
| 42 | pComp->Value(rStruct: mkNamingAdapt(rValue&: sLeagueProgressData, szName: "LeagueProgressData" , rDefault: StdStrBuf())); |
| 43 | StdEnumEntry<LeagueStatus> LeagueStatusEntries[] = |
| 44 | { |
| 45 | { .Name: "" , .Val: RRPLS_Unknown }, |
| 46 | { .Name: "Lost" , .Val: RRPLS_Lost }, |
| 47 | { .Name: "Won" , .Val: RRPLS_Won }, |
| 48 | }; |
| 49 | pComp->Value(rStruct: mkNamingAdapt(rValue: mkEnumAdaptT<uint8_t>(rVal&: eLeagueStatus, pNames: LeagueStatusEntries), szName: "Status" , rDefault: RRPLS_Unknown)); // name used in league reply! |
| 50 | } |
| 51 | |
| 52 | void C4RoundResultsPlayer::EvaluatePlayer(C4Player *pPlr) |
| 53 | { |
| 54 | assert(pPlr); |
| 55 | // set fields by player |
| 56 | iTotalPlayingTime = pPlr->TotalPlayingTime; |
| 57 | if (pPlr->Evaluated) |
| 58 | { |
| 59 | iScoreNew = pPlr->Score; |
| 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->Score; |
| 66 | } |
| 67 | // load icon from player |
| 68 | fctBigIcon.Clear(); |
| 69 | if (pPlr->BigIcon.Surface) |
| 70 | { |
| 71 | fctBigIcon.Create(iWdt: pPlr->BigIcon.Wdt, iHgt: pPlr->BigIcon.Hgt); |
| 72 | pPlr->BigIcon.Draw(cgo&: fctBigIcon); |
| 73 | } |
| 74 | // progress data by player |
| 75 | C4PlayerInfo *pInfo = pPlr->GetInfo(); |
| 76 | if (pInfo) |
| 77 | { |
| 78 | sLeagueProgressData.Copy(pnData: pInfo->GetLeagueProgressData()); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | void C4RoundResultsPlayer::EvaluateLeague(C4RoundResultsPlayer *pLeaguePlayerInfo) |
| 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(pnData: " " ); |
| 97 | sCustomEvaluationStrings.Append(pnData: szCustomString); |
| 98 | } |
| 99 | |
| 100 | bool C4RoundResultsPlayer::operator==(const C4RoundResultsPlayer &cmp) const |
| 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 | return true; |
| 114 | } |
| 115 | |
| 116 | C4RoundResultsPlayer &C4RoundResultsPlayer::operator=(const C4RoundResultsPlayer &cpy) |
| 117 | { |
| 118 | if (this == &cpy) return *this; |
| 119 | // assign all xcept icon |
| 120 | id = cpy.id; |
| 121 | iTotalPlayingTime = cpy.iTotalPlayingTime; |
| 122 | iScoreOld = cpy.iScoreOld; |
| 123 | iScoreNew = cpy.iScoreNew; |
| 124 | sCustomEvaluationStrings = cpy.sCustomEvaluationStrings; |
| 125 | iLeagueScoreNew = cpy.iLeagueScoreNew; |
| 126 | iLeagueScoreGain = cpy.iLeagueScoreGain; |
| 127 | iLeagueRankNew = cpy.iLeagueRankNew; |
| 128 | iLeagueRankSymbolNew = cpy.iLeagueRankSymbolNew; |
| 129 | sLeagueProgressData = cpy.sLeagueProgressData; |
| 130 | eLeagueStatus = cpy.eLeagueStatus; |
| 131 | return *this; |
| 132 | } |
| 133 | |
| 134 | // C4RoundResultsPlayers |
| 135 | |
| 136 | void C4RoundResultsPlayers::Clear() |
| 137 | { |
| 138 | while (iPlayerCount) delete ppPlayers[--iPlayerCount]; |
| 139 | delete[] ppPlayers; |
| 140 | ppPlayers = nullptr; |
| 141 | iPlayerCapacity = 0; |
| 142 | } |
| 143 | |
| 144 | void C4RoundResultsPlayers::CompileFunc(StdCompiler *pComp) |
| 145 | { |
| 146 | bool fCompiler = pComp->isCompiler(); |
| 147 | if (fCompiler) Clear(); |
| 148 | int32_t iTemp = iPlayerCount; |
| 149 | pComp->Value(rStruct: mkNamingCountAdapt<int32_t>(iCount&: iTemp, szName: "Player" )); |
| 150 | if (iTemp < 0 || iTemp > C4MaxPlayer) |
| 151 | { |
| 152 | pComp->excCorrupt(message: "player count out of range" ); return; |
| 153 | } |
| 154 | // Grow list, if necessary |
| 155 | if (fCompiler && iTemp > iPlayerCapacity) |
| 156 | { |
| 157 | GrowList(iByVal: iTemp - iPlayerCapacity); |
| 158 | iPlayerCount = iTemp; |
| 159 | std::fill_n(first: ppPlayers, n: iPlayerCount, value: nullptr); |
| 160 | } |
| 161 | // Compile |
| 162 | pComp->Value(rStruct: mkNamingAdapt(rValue: mkArrayAdaptMapS(array: ppPlayers, size: iPlayerCount, map: mkPtrAdaptNoNull<C4RoundResultsPlayer>), szName: "Player" )); |
| 163 | // Force specialization |
| 164 | mkPtrAdaptNoNull<C4RoundResultsPlayer>(rpObj&: *ppPlayers); |
| 165 | } |
| 166 | |
| 167 | C4RoundResultsPlayer *C4RoundResultsPlayers::GetByIndex(int32_t idx) const |
| 168 | { |
| 169 | if (idx >= 0 && idx < iPlayerCount) |
| 170 | return ppPlayers[idx]; |
| 171 | else |
| 172 | return nullptr; |
| 173 | } |
| 174 | |
| 175 | C4RoundResultsPlayer *C4RoundResultsPlayers::GetByID(int32_t id) const |
| 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(dest: ppNew, src: ppPlayers, n: iPlayerCount * sizeof(C4RoundResultsPlayer *)); |
| 191 | } |
| 192 | delete[] ppPlayers; |
| 193 | // assign new |
| 194 | ppPlayers = ppNew; |
| 195 | } |
| 196 | |
| 197 | void C4RoundResultsPlayers::Add(C4RoundResultsPlayer *pNewPlayer) |
| 198 | { |
| 199 | assert(pNewPlayer); |
| 200 | if (iPlayerCount == iPlayerCapacity) GrowList(iByVal: 4); |
| 201 | ppPlayers[iPlayerCount++] = pNewPlayer; |
| 202 | } |
| 203 | |
| 204 | C4RoundResultsPlayer *C4RoundResultsPlayers::GetCreateByID(int32_t id) |
| 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(pNewPlayer: pPlr); |
| 215 | } |
| 216 | return pPlr; |
| 217 | } |
| 218 | |
| 219 | bool C4RoundResultsPlayers::operator==(const C4RoundResultsPlayers &cmp) const |
| 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 | |
| 229 | C4RoundResultsPlayers &C4RoundResultsPlayers::operator=(const C4RoundResultsPlayers &cpy) |
| 230 | { |
| 231 | Clear(); |
| 232 | C4RoundResultsPlayer *pPlr; int32_t i = 0; |
| 233 | while (pPlr = cpy.GetByIndex(idx: i++)) |
| 234 | Add(pNewPlayer: new C4RoundResultsPlayer(*pPlr)); |
| 235 | return *this; |
| 236 | } |
| 237 | |
| 238 | // C4RoundResults |
| 239 | |
| 240 | void C4RoundResults::Init() |
| 241 | { |
| 242 | if (Game.C4S.Game.IsMelee()) |
| 243 | fHideSettlementScore = true; |
| 244 | else fHideSettlementScore = false; |
| 245 | } |
| 246 | |
| 247 | void C4RoundResults::Clear() |
| 248 | { |
| 249 | Players.Clear(); |
| 250 | Goals.Clear(); |
| 251 | iPlayingTime = 0; |
| 252 | sCustomEvaluationStrings.Clear(); |
| 253 | iLeaguePerformance = 0; |
| 254 | sNetResult.Clear(); |
| 255 | eNetResult = NR_None; |
| 256 | fHideSettlementScore = false; |
| 257 | } |
| 258 | |
| 259 | void C4RoundResults::CompileFunc(StdCompiler *pComp) |
| 260 | { |
| 261 | bool fCompiler = pComp->isCompiler(); |
| 262 | if (fCompiler) Clear(); |
| 263 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Goals, szName: "Goals" , rDefault: C4IDList())); |
| 264 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iPlayingTime, szName: "PlayingTime" , rDefault: 0u)); |
| 265 | pComp->Value(rStruct: mkNamingAdapt(rValue&: fHideSettlementScore, szName: "HideSettlementScore" , rDefault: !!Game.C4S.Game.IsMelee())); |
| 266 | pComp->Value(rStruct: mkNamingAdapt(rValue&: sCustomEvaluationStrings, szName: "CustomEvaluationStrings" , rDefault: StdStrBuf())); |
| 267 | pComp->Value(rStruct: mkNamingAdapt(rValue&: iLeaguePerformance, szName: "LeaguePerformance" , rDefault: 0)); |
| 268 | pComp->Value(rStruct: mkNamingAdapt(rValue&: Players, szName: "PlayerInfos" , rDefault: C4RoundResultsPlayers())); |
| 269 | pComp->Value(rStruct: mkNamingAdapt(rValue&: sNetResult, szName: "NetResult" , rDefault: StdStrBuf())); |
| 270 | StdEnumEntry<NetResult> NetResultEntries[] = |
| 271 | { |
| 272 | { .Name: "" , .Val: NR_None }, |
| 273 | { .Name: "LeagueOK" , .Val: NR_LeagueOK }, |
| 274 | { .Name: "LeagueError" , .Val: NR_LeagueError }, |
| 275 | { .Name: "NetError" , .Val: NR_NetError }, |
| 276 | }; |
| 277 | pComp->Value(rStruct: mkNamingAdapt(rValue: mkEnumAdaptT<uint8_t>(rVal&: eNetResult, pNames: NetResultEntries), szName: "NetResult" , rDefault: NR_None)); |
| 278 | } |
| 279 | |
| 280 | void C4RoundResults::EvaluateGoals(C4IDList &GoalList, C4IDList &FulfilledGoalList, int32_t iPlayerNumber) |
| 281 | { |
| 282 | // clear prev |
| 283 | GoalList.Clear(); FulfilledGoalList.Clear(); |
| 284 | // Items |
| 285 | bool fRivalvry = !!Game.ObjectCount(id: C4Id(str: "RVLR" )); |
| 286 | int32_t cnt; C4ID idGoal; |
| 287 | for (cnt = 0; idGoal = Game.Objects.GetListID(dwCategory: C4D_Goal, Index: cnt); cnt++) |
| 288 | { |
| 289 | // determine if the goal is fulfilled - do the calls even if the menu is not to be opened to ensure synchronization |
| 290 | bool fFulfilled = false; |
| 291 | C4Object *pObj; |
| 292 | if (pObj = Game.Objects.Find(id: idGoal)) |
| 293 | { |
| 294 | if (fRivalvry) |
| 295 | { |
| 296 | fFulfilled = static_cast<bool>(pObj->Call(PSF_IsFulfilledforPlr, pPars: {C4VInt(iVal: iPlayerNumber)})); |
| 297 | } |
| 298 | else |
| 299 | fFulfilled = static_cast<bool>(pObj->Call(PSF_IsFulfilled)); |
| 300 | } |
| 301 | GoalList.SetIDCount(id: idGoal, count: cnt, addNewID: true); |
| 302 | if (fFulfilled) FulfilledGoalList.SetIDCount(id: idGoal, count: 1, addNewID: true); |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | void C4RoundResults::EvaluateGame() |
| 307 | { |
| 308 | // set game data |
| 309 | C4Player *pFirstLocalPlayer = Game.Players.GetLocalByIndex(iIndex: 0); |
| 310 | int32_t iFirstLocalPlayer = pFirstLocalPlayer ? pFirstLocalPlayer->Number : NO_OWNER; |
| 311 | EvaluateGoals(GoalList&: Goals, FulfilledGoalList&: FulfilledGoals, iPlayerNumber: iFirstLocalPlayer); |
| 312 | iPlayingTime = Game.Time; |
| 313 | } |
| 314 | |
| 315 | void C4RoundResults::EvaluateNetwork(C4RoundResults::NetResult eNetResult, const char *szResultMsg) |
| 316 | { |
| 317 | // take result only if there was no previous result (the previous one is usually more specific) |
| 318 | if (!HasNetResult()) |
| 319 | { |
| 320 | this->eNetResult = eNetResult; |
| 321 | if (szResultMsg) sNetResult.Copy(pnData: szResultMsg); else sNetResult.Clear(); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | void C4RoundResults::EvaluateLeague(const char *szResultMsg, bool fSuccess, const C4RoundResultsPlayers &rLeagueInfo) |
| 326 | { |
| 327 | // League evaluation imples network evaluation |
| 328 | Game.RoundResults.EvaluateNetwork(eNetResult: fSuccess ? C4RoundResults::NR_LeagueOK : C4RoundResults::NR_LeagueError, szResultMsg); |
| 329 | // Evaluation called by league: Sets new league scores and ranks |
| 330 | C4RoundResultsPlayer *pPlr, *pOwnPlr; int32_t i = 0; |
| 331 | while (pPlr = rLeagueInfo.GetByIndex(idx: i++)) |
| 332 | { |
| 333 | pOwnPlr = Players.GetCreateByID(id: pPlr->GetID()); |
| 334 | pOwnPlr->EvaluateLeague(pLeaguePlayerInfo: pPlr); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | void C4RoundResults::EvaluatePlayer(C4Player *pPlr) |
| 339 | { |
| 340 | // Evaluation called by player when it's evaluated |
| 341 | assert(pPlr); |
| 342 | C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(id: pPlr->ID); |
| 343 | pOwnPlr->EvaluatePlayer(pPlr); |
| 344 | } |
| 345 | |
| 346 | void C4RoundResults::AddCustomEvaluationString(const char *szCustomString, int32_t idPlayer) |
| 347 | { |
| 348 | // Set custom string to be shown in game over dialog |
| 349 | // idPlayer==0 for global strings |
| 350 | if (!idPlayer) |
| 351 | { |
| 352 | if (sCustomEvaluationStrings.getLength()) sCustomEvaluationStrings.AppendChar(cChar: '|'); |
| 353 | sCustomEvaluationStrings.Append(pnData: szCustomString); |
| 354 | } |
| 355 | else |
| 356 | { |
| 357 | C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(id: idPlayer); |
| 358 | pOwnPlr->AddCustomEvaluationString(szCustomString); |
| 359 | } |
| 360 | } |
| 361 | |
| 362 | void C4RoundResults::HideSettlementScore(bool fHide) |
| 363 | { |
| 364 | fHideSettlementScore = fHide; |
| 365 | } |
| 366 | |
| 367 | bool C4RoundResults::SettlementScoreIsHidden() |
| 368 | { |
| 369 | return fHideSettlementScore; |
| 370 | } |
| 371 | |
| 372 | void C4RoundResults::SetLeaguePerformance(int32_t iNewPerf, int32_t idPlayer) |
| 373 | { |
| 374 | // Store to be sent later. idPlayer == 0 means global performance. |
| 375 | if (!idPlayer) |
| 376 | { |
| 377 | iLeaguePerformance = iNewPerf; |
| 378 | } |
| 379 | else |
| 380 | { |
| 381 | C4RoundResultsPlayer *pOwnPlr = Players.GetCreateByID(id: idPlayer); |
| 382 | pOwnPlr->SetLeaguePerformance(iNewPerf); |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | int32_t C4RoundResults::GetLeaguePerformance(int32_t idPlayer) const |
| 387 | { |
| 388 | if (!idPlayer) |
| 389 | return iLeaguePerformance; |
| 390 | else if (C4RoundResultsPlayer *pPlr = Players.GetByID(id: idPlayer)) |
| 391 | return pPlr->GetLeaguePerformance(); |
| 392 | return 0; |
| 393 | } |
| 394 | |
| 395 | bool C4RoundResults::Load(C4Group &hGroup, const char *szFilename) |
| 396 | { |
| 397 | // clear previous |
| 398 | Clear(); |
| 399 | // load file contents |
| 400 | StdStrBuf Buf; |
| 401 | if (!hGroup.LoadEntryString(szEntryName: szFilename, Buf)) return false; |
| 402 | // compile |
| 403 | if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(TargetStruct: mkNamingAdapt(rValue&: *this, szName: "RoundResults" ), SrcBuf: Buf, szName: szFilename)) return false; |
| 404 | // done, success |
| 405 | return true; |
| 406 | } |
| 407 | |
| 408 | bool C4RoundResults::Save(C4Group &hGroup, const char *szFilename) |
| 409 | { |
| 410 | // remove previous entry from group |
| 411 | hGroup.DeleteEntry(szFilename); |
| 412 | // decompile |
| 413 | try |
| 414 | { |
| 415 | const std::string buf{DecompileToBuf<StdCompilerINIWrite>(SrcStruct: mkNamingAdapt(rValue&: *this, szName: "RoundResults" ))}; |
| 416 | // save it, if not empty |
| 417 | if (!buf.empty()) |
| 418 | { |
| 419 | StdStrBuf copy{buf.c_str(), buf.size()}; |
| 420 | if (!hGroup.Add(szName: szFilename, pBuffer&: copy, fChild: false, fHoldBuffer: true)) |
| 421 | return false; |
| 422 | } |
| 423 | } |
| 424 | catch (const StdCompiler::Exception &) |
| 425 | { |
| 426 | return false; |
| 427 | } |
| 428 | // done, success |
| 429 | return true; |
| 430 | } |
| 431 | |
| 432 | // C4PacketLeagueRoundResults |
| 433 | |
| 434 | void C4PacketLeagueRoundResults::CompileFunc(StdCompiler *pComp) |
| 435 | { |
| 436 | pComp->Value(rStruct: mkNamingAdapt(rValue&: fSuccess, szName: "Success" , rDefault: false)); |
| 437 | pComp->Value(rStruct: mkNamingAdapt(rValue&: sResultsString, szName: "ResultString" , rDefault: StdStrBuf())); |
| 438 | pComp->Value(rStruct&: Players); |
| 439 | } |
| 440 | |