1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017-2021, The LegacyClonk 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
17/* Structures for object and player info components */
18
19#include <C4InfoCore.h>
20
21#include <C4Random.h>
22#include <C4RankSystem.h>
23#include "StdMarkup.h"
24#include <C4Group.h>
25#include <C4Components.h>
26#include <C4Wrappers.h>
27
28#include <C4Random.h>
29
30// Name File Handling
31
32char GetANameBuffer[C4MaxName + 1];
33
34const char *GetAName(const char *szNameFile)
35{
36 // always eat the Random-value, so having or not having a Names.txt makes no difference
37 int iName = Random(iRange: 1000);
38
39 FILE *hNamefile;
40
41 if (!szNameFile) return "Clonk";
42 if (!(hNamefile = fopen(filename: szNameFile, modes: "r"))) return "Clonk";
43
44 for (int iCnt = 0; iCnt < iName; iCnt++)
45 AdvanceFileLine(fhnd: hNamefile);
46 GetANameBuffer[0] = 0; int iLoops = 0;
47 do
48 {
49 if (!ReadFileLine(fhnd: hNamefile, tobuf: GetANameBuffer, maxlen: C4MaxName))
50 {
51 rewind(stream: hNamefile); iLoops++;
52 }
53 } while ((iLoops < 2) && (!GetANameBuffer[0] || (GetANameBuffer[0] == '#') || (GetANameBuffer[0] == ' ')));
54 fclose(stream: hNamefile);
55 if (iLoops >= 2) return "Clonk";
56 return GetANameBuffer;
57}
58
59// Player Info
60
61C4PlayerInfoCore::C4PlayerInfoCore()
62{
63 Default();
64}
65
66void C4PlayerInfoCore::Default(C4RankSystem *pRanks)
67{
68 Rank = 0;
69 SCopy(szSource: "Neuling", sTarget: PrefName);
70 Comment[0] = 0;
71 if (pRanks) SCopy(szSource: pRanks->GetRankName(iRank: Rank, fReturnLastIfOver: false).getData(), sTarget: RankName);
72 else SCopy(szSource: "Rang", sTarget: RankName);
73 Score = 0;
74 Rounds = 0;
75 RoundsWon = 0;
76 RoundsLost = 0;
77 TotalPlayingTime = 0;
78 PrefColor = 0;
79 PrefColorDw = 0xff;
80 PrefColor2Dw = 0;
81 PrefControl = C4P_Control_Keyboard1;
82 PrefPosition = 0;
83 PrefMouse = 1;
84 PrefControlStyle = 0;
85 PrefAutoContextMenu = 0;
86 ExtraData.Reset();
87 LastRound.Default();
88}
89
90uint32_t C4PlayerInfoCore::GetPrefColorValue(int32_t iPrefColor)
91{
92 uint32_t valRGB[12] =
93 {
94 0x0000E8, 0xF40000, 0x00C800, 0xFCF41C,
95 0xC48444, 0x784830, 0xA04400, 0xF08050,
96 0x848484, 0xFFFFFF, 0x0094F8, 0xBC00C0
97 };
98 if (Inside<int32_t>(ival: iPrefColor, lbound: 0, rbound: 11))
99 return valRGB[iPrefColor];
100 return 0xAAAAAA;
101}
102
103bool C4PlayerInfoCore::Load(C4Group &hGroup)
104{
105 // New version
106 StdStrBuf Source;
107 if (hGroup.LoadEntryString(C4CFN_PlayerInfoCore, Buf&: Source))
108 {
109 // Compile
110 StdStrBuf GrpName = hGroup.GetFullName(); GrpName.Append(DirSep C4CFN_PlayerInfoCore);
111 if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(TargetStruct&: *this, SrcBuf: Source, szName: GrpName.getData()))
112 return false;
113 // Pref for AutoContextMenus is still undecided: default by player's control style
114 if (PrefAutoContextMenu == -1)
115 PrefAutoContextMenu = PrefControlStyle;
116 // Determine true color from indexed pref color
117 if (!PrefColorDw)
118 PrefColorDw = GetPrefColorValue(iPrefColor: PrefColor);
119 // Validate colors
120 PrefColorDw &= 0xffffff;
121 PrefColor2Dw &= 0xffffff;
122 // Validate name
123 CMarkup::StripMarkup(szText: PrefName);
124 // Success
125 return true;
126 }
127
128 // Old version no longer supported - sorry
129 return false;
130}
131
132bool C4PlayerInfoCore::Save(C4Group &hGroup)
133{
134 std::string source;
135
136 StdStrBuf name{hGroup.GetFullName()};
137 name.Append(DirSep C4CFN_PlayerInfoCore);
138
139 if (!DecompileToBuf_Log<StdCompilerINIWrite>(TargetStruct&: *this, pOut: &source, szName: name.getData()))
140 return false;
141 StdStrBuf buf{source.c_str(), source.size()};
142 if (!hGroup.Add(C4CFN_PlayerInfoCore, pBuffer&: buf, fChild: false, fHoldBuffer: true))
143 return false;
144 hGroup.Delete(szFiles: "C4Player.c4b");
145 return true;
146}
147
148void C4PlayerInfoCore::CompileFunc(StdCompiler *pComp)
149{
150 {
151 const auto name = pComp->Name(szName: "Player");
152 pComp->Value(rStruct: mkNamingAdapt(toC4CStr(PrefName), szName: "Name", rDefault: "Neuling"));
153 pComp->Value(rStruct: mkNamingAdapt(toC4CStr(Comment), szName: "Comment", rDefault: ""));
154 pComp->Value(rStruct: mkNamingAdapt(rValue&: Rank, szName: "Rank", rDefault: 0));
155 pComp->Value(rStruct: mkNamingAdapt(toC4CStr(RankName), szName: "RankName", rDefault: LoadResStr(id: C4ResStrTableKey::IDS_MSG_RANK)));
156 pComp->Value(rStruct: mkNamingAdapt(rValue&: Score, szName: "Score", rDefault: 0));
157 pComp->Value(rStruct: mkNamingAdapt(rValue&: Rounds, szName: "Rounds", rDefault: 0));
158 pComp->Value(rStruct: mkNamingAdapt(rValue&: RoundsWon, szName: "RoundsWon", rDefault: 0));
159 pComp->Value(rStruct: mkNamingAdapt(rValue&: RoundsLost, szName: "RoundsLost", rDefault: 0));
160 pComp->Value(rStruct: mkNamingAdapt(rValue&: TotalPlayingTime, szName: "TotalPlayingTime", rDefault: 0));
161 pComp->Value(rStruct: mkNamingAdapt(rValue&: ExtraData, szName: "ExtraData", rDefault: C4ValueMapData()));
162 }
163
164 {
165 const auto name = pComp->Name(szName: "Preferences");
166 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefColor, szName: "Color", rDefault: 0));
167 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefColorDw, szName: "ColorDw", rDefault: 0xffu));
168 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefColor2Dw, szName: "AlternateColorDw", rDefault: 0u));
169 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefControl, szName: "Control", rDefault: C4P_Control_Keyboard2));
170 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefControlStyle, szName: "AutoStopControl", rDefault: 0));
171 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefAutoContextMenu, szName: "AutoContextMenu", rDefault: -1)); // compiling default is -1 (if this is detected, AutoContextMenus will be defaulted by control style)
172 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefPosition, szName: "Position", rDefault: 0));
173 pComp->Value(rStruct: mkNamingAdapt(rValue&: PrefMouse, szName: "Mouse", rDefault: 1));
174 }
175
176 pComp->Value(rStruct: mkNamingAdapt(rValue&: LastRound, szName: "LastRound"));
177}
178
179// Physical Info
180
181struct C4PhysInfoNameMap_t { const char *szName; C4PhysicalInfo::Offset off; } C4PhysInfoNameMap[] =
182{
183 { .szName: "Energy", .off: &C4PhysicalInfo::Energy },
184 { .szName: "Breath", .off: &C4PhysicalInfo::Breath },
185 { .szName: "Walk", .off: &C4PhysicalInfo::Walk },
186 { .szName: "Jump", .off: &C4PhysicalInfo::Jump },
187 { .szName: "Scale", .off: &C4PhysicalInfo::Scale },
188 { .szName: "Hangle", .off: &C4PhysicalInfo::Hangle },
189 { .szName: "Dig", .off: &C4PhysicalInfo::Dig },
190 { .szName: "Swim", .off: &C4PhysicalInfo::Swim },
191 { .szName: "Throw", .off: &C4PhysicalInfo::Throw },
192 { .szName: "Push", .off: &C4PhysicalInfo::Push },
193 { .szName: "Fight", .off: &C4PhysicalInfo::Fight },
194 { .szName: "Magic", .off: &C4PhysicalInfo::Magic },
195 { .szName: "Float", .off: &C4PhysicalInfo::Float },
196 { .szName: "CanScale", .off: &C4PhysicalInfo::CanScale },
197 { .szName: "CanHangle", .off: &C4PhysicalInfo::CanHangle },
198 { .szName: "CanDig", .off: &C4PhysicalInfo::CanDig },
199 { .szName: "CanConstruct", .off: &C4PhysicalInfo::CanConstruct },
200 { .szName: "CanChop", .off: &C4PhysicalInfo::CanChop },
201 { .szName: "CanFly", .off: &C4PhysicalInfo::CanFly },
202 { .szName: "CorrosionResist", .off: &C4PhysicalInfo::CorrosionResist },
203 { .szName: "BreatheWater", .off: &C4PhysicalInfo::BreatheWater },
204 { .szName: nullptr, .off: nullptr }
205};
206
207void C4PhysicalInfo::PromotionUpdate(int32_t iRank, bool fUpdateTrainablePhysicals, C4Def *pTrainDef)
208{
209 if (iRank >= 0) { CanDig = 1; CanChop = 1; CanConstruct = 1; }
210 if (iRank >= 0) { CanScale = 1; }
211 if (iRank >= 0) { CanHangle = 1; }
212 Energy = std::max<int32_t>(a: Energy, b: (50 + 5 * BoundBy<int32_t>(bval: iRank, lbound: 0, rbound: 10)) * C4MaxPhysical / 100);
213 if (fUpdateTrainablePhysicals && pTrainDef)
214 {
215 // do standard training: Expect everything to be trained fully at rank 20
216 int32_t iTrainRank = BoundBy<int32_t>(bval: iRank, lbound: 0, rbound: 20);
217 Scale = pTrainDef->Physical.Scale + (C4MaxPhysical - pTrainDef->Physical.Scale) * iTrainRank / 20;
218 Hangle = pTrainDef->Physical.Hangle + (C4MaxPhysical - pTrainDef->Physical.Hangle) * iTrainRank / 20;
219 Swim = pTrainDef->Physical.Swim + (C4MaxPhysical - pTrainDef->Physical.Swim) * iTrainRank / 20;
220 Fight = pTrainDef->Physical.Fight + (C4MaxPhysical - pTrainDef->Physical.Fight) * iTrainRank / 20;
221 // do script updates for any physicals as required (this will train stuff like magic)
222 const char *szPhysName; C4PhysicalInfo::Offset PhysOff;
223 for (int32_t iPhysIdx = 0; szPhysName = GetNameByIndex(iIdx: iPhysIdx, pmpiOut: &PhysOff); ++iPhysIdx)
224 {
225 C4Value PhysVal(this->*PhysOff, C4V_Int);
226 if (pTrainDef->Script.Call(PSF_GetFairCrewPhysical, pPars: {C4VString(strString: szPhysName), C4VInt(iVal: iRank), C4VRef(pVal: &PhysVal)}))
227 {
228 this->*PhysOff = PhysVal.getInt();
229 }
230 }
231 }
232}
233
234C4PhysicalInfo::C4PhysicalInfo()
235{
236 Default();
237}
238
239void C4PhysicalInfo::Default()
240{
241 std::memset(s: this, c: 0, n: sizeof(C4PhysicalInfo));
242}
243
244bool C4PhysicalInfo::GetOffsetByName(const char *szPhysicalName, Offset *pmpiOut)
245{
246 // query map
247 for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
248 if (SEqual(szStr1: entry->szName, szStr2: szPhysicalName))
249 {
250 *pmpiOut = entry->off;
251 return true;
252 }
253 return false;
254}
255
256const char *C4PhysicalInfo::GetNameByOffset(Offset mpiOff)
257{
258 // query map
259 for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
260 if (entry->off == mpiOff)
261 return entry->szName;
262 return nullptr;
263}
264
265const char *C4PhysicalInfo::GetNameByIndex(int32_t iIdx, Offset *pmpiOut)
266{
267 // query map
268 if (!Inside<int32_t>(ival: iIdx, lbound: 0, rbound: sizeof(C4PhysInfoNameMap) / sizeof(C4PhysInfoNameMap_t))) return nullptr;
269 if (pmpiOut) *pmpiOut = C4PhysInfoNameMap[iIdx].off;
270 return C4PhysInfoNameMap[iIdx].szName;
271}
272
273void C4PhysicalInfo::CompileFunc(StdCompiler *pComp)
274{
275 for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
276 pComp->Value(rStruct: mkNamingAdapt(rValue&: (this->*(entry->off)), szName: entry->szName, rDefault: 0));
277}
278
279void C4PhysicalInfo::TrainValue(int32_t *piVal, int32_t iTrainBy, int32_t iMaxTrain)
280{
281 // only do training if value was nonzero before (e.g., Magic for revaluated Clonks)
282 if (*piVal)
283 // do train value: Do not increase above maximum, but never decrease either
284 *piVal = (std::max)(a: (std::min)(a: *piVal + iTrainBy, b: iMaxTrain), b: *piVal);
285}
286
287void C4PhysicalInfo::Train(Offset mpiOffset, int32_t iTrainBy, int32_t iMaxTrain)
288{
289 // train own value
290 TrainValue(piVal: &(this->*mpiOffset), iTrainBy, iMaxTrain);
291}
292
293bool C4PhysicalInfo::operator==(const C4PhysicalInfo &cmp) const
294{
295 // all fields must be equal
296 for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
297 if (this->*(entry->off) != cmp.*(entry->off))
298 return false;
299 return true;
300}
301
302void C4TempPhysicalInfo::CompileFunc(StdCompiler *pComp)
303{
304 C4PhysicalInfo::CompileFunc(pComp);
305
306 pComp->Value(rStruct: mkNamingAdapt(rValue: mkSTLContainerAdapt(rTarget&: Changes), szName: "Changes", rDefault: std::vector<C4PhysicalChange>()));
307}
308
309void C4TempPhysicalInfo::Train(Offset mpiOffset, int32_t iTrainBy, int32_t iMaxTrain)
310{
311 // train own value
312 C4PhysicalInfo::Train(mpiOffset, iTrainBy, iMaxTrain);
313 // train all temp values
314 for (std::vector<C4PhysicalChange>::iterator i = Changes.begin(); i != Changes.end(); ++i)
315 if (i->mpiOffset == mpiOffset)
316 TrainValue(piVal: &(i->PrevVal), iTrainBy, iMaxTrain);
317}
318
319bool C4TempPhysicalInfo::HasChanges(C4PhysicalInfo *pRefPhysical)
320{
321 // always return true if there are temp changes
322 if (!Changes.empty()) return true;
323 // also return true if any value deviates from the reference
324 if (pRefPhysical)
325 {
326 if (!(*pRefPhysical == *this)) return true;
327 }
328
329 // no change known
330 return false;
331}
332
333void C4TempPhysicalInfo::RegisterChange(C4PhysicalInfo::Offset mpiOffset)
334{
335 // append physical change to list
336 Changes.push_back(x: C4PhysicalChange(this->*mpiOffset, mpiOffset));
337}
338
339bool C4TempPhysicalInfo::ResetPhysical(C4PhysicalInfo::Offset mpiOffset)
340{
341 // search last matching physical check (should always be last if well scripted)
342 for (std::vector<C4PhysicalChange>::reverse_iterator i = Changes.rbegin(); i != Changes.rend(); ++i)
343 if ((*i).mpiOffset == mpiOffset)
344 {
345 this->*mpiOffset = (*i).PrevVal;
346 Changes.erase(position: (i + 1).base());
347 return true;
348 }
349
350 return false;
351}
352
353void C4PhysicalChange::CompileFunc(StdCompiler *pComp)
354{
355 // name=oldval
356 char phyn[C4MaxName + 1];
357 const char *szPhyn = C4PhysicalInfo::GetNameByOffset(mpiOff: mpiOffset);
358 if (szPhyn) SCopy(szSource: szPhyn, sTarget: phyn, iMaxL: C4MaxName); else *phyn = '\0';
359 pComp->Value(rStruct: mkStringAdapt(szString: phyn, iMaxLength: C4MaxName, eRawType: StdCompiler::RCT_Idtf));
360 if (!C4PhysicalInfo::GetOffsetByName(szPhysicalName: phyn, pmpiOut: &mpiOffset)) pComp->excNotFound(message: "Physical change name \"{}\" not found.", args: phyn);
361 pComp->Separator(eSep: StdCompiler::SEP_SET);
362 pComp->Value(rInt&: PrevVal);
363}
364
365// Object Info
366
367C4ObjectInfoCore::C4ObjectInfoCore()
368{
369 Default();
370}
371
372void C4ObjectInfoCore::Default(C4ID n_id,
373 C4DefList *pDefs,
374 const char *cpNames)
375{
376 // Def
377 C4Def *pDef = nullptr;
378 if (pDefs) pDef = pDefs->ID2Def(id: n_id);
379
380 // Defaults
381 id = n_id;
382 Participation = 1;
383 Rank = 0;
384 Experience = 0;
385 Rounds = 0;
386 DeathCount = 0;
387 Birthday = 0;
388 TotalPlayingTime = 0;
389 SCopy(szSource: "Clonk", sTarget: Name, iMaxL: C4MaxName);
390 SCopy(szSource: "Clonk", sTarget: TypeName, iMaxL: C4MaxName);
391 sRankName.Copy(pnData: "Clonk");
392 sNextRankName.Clear();
393 NextRankExp = 0;
394 DeathMessage[0] = '\0';
395 *PortraitFile = 0;
396 Age = 0;
397 ExtraData.Reset();
398
399 // Type
400 if (pDef) SCopy(szSource: pDef->GetName(), sTarget: TypeName, iMaxL: C4MaxName);
401
402 // Name
403 if (cpNames)
404 {
405 // Name file reference
406 if (SSearchNoCase(szString: cpNames, C4CFN_Names))
407 SCopy(szSource: GetAName(szNameFile: cpNames), sTarget: Name, iMaxL: C4MaxName);
408 // Name list
409 else
410 {
411 SCopySegment(fstr: cpNames, segn: Random(iRange: SCharCount(cTarget: 0x0A, szInStr: cpNames)), tstr: Name, sepa: 0x0A, iMaxL: C4MaxName + 1);
412 SClearFrontBack(szString: Name);
413 SReplaceChar(str: Name, fc: 0x0D, tc: 0x00);
414 }
415 if (!Name[0]) SCopy(szSource: "Clonk", sTarget: Name, iMaxL: C4MaxName);
416 }
417
418 if (pDefs) UpdateCustomRanks(pDefs);
419
420 // Physical
421 Physical.Default();
422 if (pDef) Physical = pDef->Physical;
423 Physical.PromotionUpdate(iRank: Rank);
424
425 // Old format
426}
427
428void C4ObjectInfoCore::Promote(int32_t iRank, C4RankSystem &rRanks, bool fForceRankName)
429{
430 Rank = iRank;
431 Physical.PromotionUpdate(iRank: Rank);
432 // copy new rank name if defined only, or forced to use highest defined rank for too high info ranks
433 StdStrBuf sNewRank(rRanks.GetRankName(iRank: Rank, fReturnLastIfOver: fForceRankName));
434 if (sNewRank) sRankName.Copy(Buf2: sNewRank);
435}
436
437void C4ObjectInfoCore::UpdateCustomRanks(C4DefList *pDefs)
438{
439 assert(pDefs);
440 C4Def *pDef = pDefs->ID2Def(id);
441 if (!pDef) return;
442 if (pDef->pRankNames)
443 {
444 StdStrBuf sRank(pDef->pRankNames->GetRankName(iRank: Rank, fReturnLastIfOver: false));
445 if (sRank) sRankName.Copy(Buf2: sRank);
446 // next rank data
447 StdStrBuf sNextRank(pDef->pRankNames->GetRankName(iRank: Rank + 1, fReturnLastIfOver: false));
448 if (sNextRank)
449 {
450 sNextRankName.Copy(Buf2: sNextRank);
451 NextRankExp = pDef->pRankNames->Experience(iRank: Rank + 1);
452 }
453 else
454 {
455 // no more promotion possible by custom rank system
456 sNextRankName.Clear();
457 NextRankExp = C4RankSystem::EXP_NoPromotion;
458 }
459 }
460 else
461 {
462 // definition does not have custom rank names
463 sNextRankName.Clear();
464 NextRankExp = 0;
465 }
466}
467
468bool C4ObjectInfoCore::GetNextRankInfo(C4RankSystem &rDefaultRanks, int32_t *piNextRankExp, StdStrBuf *psNextRankName)
469{
470 int32_t iNextRankExp;
471 // custom rank assigned?
472 if (NextRankExp)
473 {
474 iNextRankExp = NextRankExp;
475 if (psNextRankName) psNextRankName->Copy(Buf2: sNextRankName);
476 }
477 else
478 {
479 // no custom rank: Get from default set
480 StdStrBuf sRank(rDefaultRanks.GetRankName(iRank: Rank + 1, fReturnLastIfOver: false));
481 if (sRank)
482 {
483 iNextRankExp = rDefaultRanks.Experience(iRank: Rank + 1);
484 if (psNextRankName) psNextRankName->Copy(Buf2: sRank);
485 }
486 else
487 // no more promotion
488 iNextRankExp = C4RankSystem::EXP_NoPromotion;
489 }
490 // return result
491 if (piNextRankExp) *piNextRankExp = iNextRankExp;
492 // return value is whether additional promotion is possible
493 return iNextRankExp != C4RankSystem::EXP_NoPromotion;
494}
495
496bool C4ObjectInfoCore::Load(C4Group &hGroup)
497{
498 StdStrBuf Source;
499 return hGroup.LoadEntryString(C4CFN_ObjectInfoCore, Buf&: Source) &&
500 Compile(szSource: Source.getData());
501}
502
503bool C4ObjectInfoCore::Save(C4Group &hGroup, C4DefList *pDefs)
504{
505 // rank overload by def: Update any NextRank-stuff
506 if (pDefs) UpdateCustomRanks(pDefs);
507
508 std::string buf;
509 try
510 {
511 buf = DecompileToBuf<StdCompilerINIWrite>(SrcStruct: mkNamingAdapt(rValue&: *this, szName: "ObjectInfo"));
512 }
513 catch (const StdCompiler::Exception &)
514 {
515 return false;
516 }
517
518 StdStrBuf copy{buf.c_str(), buf.size()};
519 if (!hGroup.Add(C4CFN_ObjectInfoCore, pBuffer&: copy, fChild: false, fHoldBuffer: true))
520 {
521 return false;
522 }
523 return true;
524}
525
526void C4ObjectInfoCore::CompileFunc(StdCompiler *pComp)
527{
528 pComp->Value(rStruct: mkNamingAdapt(rValue: mkC4IDAdapt(rValue&: id), szName: "id", rDefault: C4ID_None));
529 pComp->Value(rStruct: mkNamingAdapt(toC4CStr(Name), szName: "Name", rDefault: "Clonk"));
530 pComp->Value(rStruct: mkNamingAdapt(toC4CStr(DeathMessage), szName: "DeathMessage", rDefault: ""));
531 pComp->Value(rStruct: mkNamingAdapt(toC4CStr(PortraitFile), szName: "PortraitFile", rDefault: ""));
532 pComp->Value(rStruct: mkNamingAdapt(rValue&: Rank, szName: "Rank", rDefault: 0));
533 pComp->Value(rStruct: mkNamingAdapt(rValue&: sRankName, szName: "RankName", rDefault: "Clonk"));
534 pComp->Value(rStruct: mkNamingAdapt(rValue&: sNextRankName, szName: "NextRankName", rDefault: ""));
535 pComp->Value(rStruct: mkNamingAdapt(toC4CStr(TypeName), szName: "TypeName", rDefault: "Clonk"));
536 pComp->Value(rStruct: mkNamingAdapt(rValue&: Participation, szName: "Participation", rDefault: 1));
537 pComp->Value(rStruct: mkNamingAdapt(rValue&: Experience, szName: "Experience", rDefault: 0));
538 pComp->Value(rStruct: mkNamingAdapt(rValue&: NextRankExp, szName: "NextRankExp", rDefault: 0));
539 pComp->Value(rStruct: mkNamingAdapt(rValue&: Rounds, szName: "Rounds", rDefault: 0));
540 pComp->Value(rStruct: mkNamingAdapt(rValue&: DeathCount, szName: "DeathCount", rDefault: 0));
541 pComp->Value(rStruct: mkNamingAdapt(rValue&: Birthday, szName: "Birthday", rDefault: 0));
542 pComp->Value(rStruct: mkNamingAdapt(rValue&: TotalPlayingTime, szName: "TotalPlayingTime", rDefault: 0));
543 pComp->Value(rStruct: mkNamingAdapt(rValue&: Age, szName: "Age", rDefault: 0));
544 pComp->Value(rStruct: mkNamingAdapt(rValue&: ExtraData, szName: "ExtraData", rDefault: C4ValueMapData()));
545
546 pComp->FollowName(szName: "Physical");
547 pComp->Value(rStruct&: Physical);
548}
549
550bool C4ObjectInfoCore::Compile(const char *szSource)
551{
552 bool ret = CompileFromBuf_LogWarn<StdCompilerINIRead>(
553 TargetStruct: mkNamingAdapt(rValue&: *this, szName: "ObjectInfo"),
554 SrcBuf: StdStrBuf::MakeRef(str: szSource),
555 szName: "ObjectInfo");
556 // Do a promotion update to set physicals right
557 Physical.PromotionUpdate(iRank: Rank);
558 // DeathMessages are not allowed to stay forever
559 if ('@' == DeathMessage[0]) DeathMessage[0] = ' ';
560 return ret;
561}
562
563// Round Info
564
565void C4RoundResult::Default()
566{
567 *this = {};
568}
569
570void C4RoundResult::CompileFunc(StdCompiler *pComp)
571{
572 pComp->Value(rStruct: mkNamingAdapt(rValue&: Title, szName: "Title", rDefault: ""));
573 pComp->Value(rStruct: mkNamingAdapt(rValue&: Date, szName: "Date", rDefault: 0u));
574 pComp->Value(rStruct: mkNamingAdapt(rValue&: Duration, szName: "Duration", rDefault: 0));
575 pComp->Value(rStruct: mkNamingAdapt(rValue&: Won, szName: "Won", rDefault: 0));
576 pComp->Value(rStruct: mkNamingAdapt(rValue&: Score, szName: "Score", rDefault: 0));
577 pComp->Value(rStruct: mkNamingAdapt(rValue&: FinalScore, szName: "FinalScore", rDefault: 0));
578 pComp->Value(rStruct: mkNamingAdapt(rValue&: TotalScore, szName: "TotalScore", rDefault: 0));
579 pComp->Value(rStruct: mkNamingAdapt(rValue&: Bonus, szName: "Bonus", rDefault: 0));
580 pComp->Value(rStruct: mkNamingAdapt(rValue&: Level, szName: "Level", rDefault: 0));
581}
582