1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk 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 /* Core component of a scenario file */
19 
20 #include "C4Include.h"
21 #include "landscape/C4Scenario.h"
22 
23 #include "c4group/C4Components.h"
24 #include "c4group/C4Group.h"
25 #include "lib/C4InputValidation.h"
26 #include "lib/C4Random.h"
27 #include "lib/StdColors.h"
28 
29 //==================================== C4SVal ==============================================
30 
C4SVal(int32_t std,int32_t rnd,int32_t min,int32_t max)31 C4SVal::C4SVal(int32_t std, int32_t rnd, int32_t min, int32_t max)
32 		: Std(std), Rnd(rnd), Min(min), Max(max)
33 {
34 }
35 
Set(int32_t std,int32_t rnd,int32_t min,int32_t max)36 void C4SVal::Set(int32_t std, int32_t rnd, int32_t min, int32_t max)
37 {
38 	Std=std; Rnd=rnd; Min=min; Max=max;
39 }
40 
SetConstant(int32_t val)41 void C4SVal::SetConstant(int32_t val)
42 {
43 	// Set to constant value and ensure limits allow it
44 	Std = val;
45 	Rnd = 0;
46 	Min = std::min<int32_t>(Min, val);
47 	Max = std::max<int32_t>(Max, val);
48 }
49 
Evaluate()50 int32_t C4SVal::Evaluate()
51 {
52 	return Clamp<int32_t>(Std+Random(2*Rnd+1)-Rnd,Min,Max);
53 }
54 
Default()55 void C4SVal::Default()
56 {
57 	Set();
58 }
59 
CompileFunc(StdCompiler * pComp)60 void C4SVal::CompileFunc(StdCompiler *pComp)
61 {
62 	pComp->Value(mkDefaultAdapt(Std, 0));
63 	if (!pComp->Separator()) return;
64 	pComp->Value(mkDefaultAdapt(Rnd, 0));
65 	if (!pComp->Separator()) return;
66 	pComp->Value(mkDefaultAdapt(Min, 0));
67 	if (!pComp->Separator()) return;
68 	pComp->Value(mkDefaultAdapt(Max, 100));
69 }
70 
71 //================================ C4Scenario ==========================================
72 
C4Scenario()73 C4Scenario::C4Scenario()
74 {
75 	Default();
76 }
77 
Default()78 void C4Scenario::Default()
79 {
80 	int32_t cnt;
81 	Head.Default();
82 	Definitions.Default();
83 	Game.Default();
84 	for (cnt=0; cnt<C4S_MaxPlayer; cnt++) PlrStart[cnt].Default();
85 	Landscape.Default();
86 	Animals.Default();
87 	Weather.Default();
88 	Game.Realism.Default();
89 	Environment.Default();
90 }
91 
Load(C4Group & hGroup,bool fLoadSection,bool suppress_errors)92 bool C4Scenario::Load(C4Group &hGroup, bool fLoadSection, bool suppress_errors)
93 {
94 	StdStrBuf Buf;
95 	if (!hGroup.LoadEntryString(C4CFN_ScenarioCore,&Buf)) return false;
96 	if (!fLoadSection) Default();
97 	if (suppress_errors)
98 	{
99 		if (!CompileFromBuf_Log<StdCompilerINIRead>(mkParAdapt(*this, fLoadSection), Buf, C4CFN_ScenarioCore))
100 		{
101 			return false;
102 		}
103 	}
104 	else
105 	{
106 		if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(mkParAdapt(*this, fLoadSection), Buf, C4CFN_ScenarioCore))
107 		{
108 			return false;
109 		}
110 	}
111 	return true;
112 }
113 
Save(C4Group & hGroup,bool fSaveSection)114 bool C4Scenario::Save(C4Group &hGroup, bool fSaveSection)
115 {
116 	StdStrBuf Buf;
117 	try
118 	{
119 		Buf.Take(DecompileToBuf<StdCompilerINIWrite>(mkParAdapt(*this, fSaveSection)));
120 	}
121 	catch (StdCompiler::Exception *)
122 		{ return false; }
123 	if (!hGroup.Add(C4CFN_ScenarioCore,Buf,false,true))
124 		{ return false; }
125 	return true;
126 }
127 
CompileFunc(StdCompiler * pComp,bool fSection)128 void C4Scenario::CompileFunc(StdCompiler *pComp, bool fSection)
129 {
130 	pComp->Value(mkNamingAdapt(mkParAdapt(Head, fSection), "Head"));
131 	if (!fSection) pComp->Value(mkNamingAdapt(Definitions, "Definitions"));
132 	pComp->Value(mkNamingAdapt(mkParAdapt(Game, fSection), "Game"));
133 	for (int32_t i = 0; i < C4S_MaxPlayer; i++)
134 		pComp->Value(mkNamingAdapt(PlrStart[i], FormatString("Player%d", i+1).getData()));
135 	pComp->Value(mkNamingAdapt(Landscape, "Landscape"));
136 	pComp->Value(mkNamingAdapt(Animals, "Animals"));
137 	pComp->Value(mkNamingAdapt(Weather, "Weather"));
138 	pComp->Value(mkNamingAdapt(Environment, "Environment"));
139 }
140 
GetMinPlayer()141 int32_t C4Scenario::GetMinPlayer()
142 {
143 	// MinPlayer is specified.
144 	if (Head.MinPlayer != 0)
145 		return Head.MinPlayer;
146 	// Otherwise/unknown: need at least one.
147 	return 1;
148 }
149 
Default()150 void C4SDefinitions::Default()
151 {
152 	LocalOnly=AllowUserChange=false;
153 	ZeroMem(Definition,sizeof (Definition));
154 	SkipDefs.Default();
155 }
156 
157 const int32_t C4S_MaxPlayerDefault = 12;
158 
Default()159 void C4SHead::Default()
160 {
161 	Origin.Clear();
162 	Icon=18;
163 	Title.clear();
164 	Loader.clear();
165 	Font.clear();
166 	Engine.clear();
167 	MissionAccess.clear();
168 	Secret = false;
169 	C4XVer[0] = C4XVer[1] = 0;
170 	Difficulty = RandomSeed = 0;
171 	SaveGame = Replay = NoInitialize = false;
172 	Film = 0;
173 	NetworkGame = NetworkRuntimeJoin = false;
174 
175 	MaxPlayer=MaxPlayerLeague=C4S_MaxPlayerDefault;
176 	MinPlayer=0; // auto-determine by mode
177 	Title = "Default Title";
178 }
179 
CompileFunc(StdCompiler * pComp,bool fSection)180 void C4SHead::CompileFunc(StdCompiler *pComp, bool fSection)
181 {
182 	if (!fSection)
183 	{
184 		pComp->Value(mkNamingAdapt(Icon,                      "Icon",                 18));
185 		pComp->Value(mkNamingAdapt(mkStringAdaptA(Title),     "Title",                "Default Title"));
186 		pComp->Value(mkNamingAdapt(mkStringAdaptA(Loader),    "Loader",               ""));
187 		pComp->Value(mkNamingAdapt(mkStringAdaptA(Font),      "Font",                 ""));
188 		pComp->Value(mkNamingAdapt(mkArrayAdaptDM(C4XVer,0),  "Version"               ));
189 		pComp->Value(mkNamingAdapt(Difficulty,                "Difficulty",           0));
190 		pComp->Value(mkNamingAdapt(MaxPlayer,                 "MaxPlayer",            C4S_MaxPlayerDefault));
191 		pComp->Value(mkNamingAdapt(MaxPlayerLeague,           "MaxPlayerLeague",      MaxPlayer));
192 		pComp->Value(mkNamingAdapt(MinPlayer,                 "MinPlayer",            0));
193 		pComp->Value(mkNamingAdapt(SaveGame,                  "SaveGame",             false));
194 		pComp->Value(mkNamingAdapt(Replay,                    "Replay",               false));
195 		pComp->Value(mkNamingAdapt(Film,                      "Film",                 0));
196 	}
197 	pComp->Value(mkNamingAdapt(NoInitialize,              "NoInitialize",         false));
198 	pComp->Value(mkNamingAdapt(RandomSeed,                "RandomSeed",           0));
199 	if (!fSection)
200 	{
201 		pComp->Value(mkNamingAdapt(mkStringAdaptA(Engine),    "Engine",               ""));
202 		pComp->Value(mkNamingAdapt(mkStringAdaptA(MissionAccess),"MissionAccess",     ""));
203 		pComp->Value(mkNamingAdapt(Secret,                    "Secret",               false));
204 		pComp->Value(mkNamingAdapt(NetworkGame,               "NetworkGame",          false));
205 		pComp->Value(mkNamingAdapt(NetworkRuntimeJoin,        "NetworkRuntimeJoin",   false));
206 		pComp->Value(mkNamingAdapt(mkStrValAdapt(mkParAdapt(Origin, StdCompiler::RCT_All), C4InVal::VAL_SubPathFilename),  "Origin",  StdCopyStrBuf()));
207 		// windows needs backslashes in Origin; other systems use forward slashes
208 		if (pComp->isDeserializer())
209 		{
210 			Origin.ReplaceChar(AltDirectorySeparator, DirectorySeparator);
211 		}
212 	}
213 }
214 
Default()215 void C4SGame::Default()
216 {
217 	Goals.Clear();
218 	Rules.Clear();
219 	FoWEnabled = true;
220 	EvaluateOnAbort = false;
221 }
222 
CompileFunc(StdCompiler * pComp,bool fSection)223 void C4SGame::CompileFunc(StdCompiler *pComp, bool fSection)
224 {
225 	if (!fSection)
226 	{
227 		pComp->Value(mkNamingAdapt(Realism.ValueOverloads,            "ValueOverloads",      C4IDList()));
228 	}
229 	pComp->Value(mkNamingAdapt(mkRuntimeValueAdapt(Realism.LandscapePushPull),         "LandscapePushPull",   false));
230 	pComp->Value(mkNamingAdapt(mkRuntimeValueAdapt(Realism.LandscapeInsertThrust),     "LandscapeInsertThrust",true));
231 
232 	pComp->Value(mkNamingAdapt(mkParAdapt(Mode, StdCompiler::RCT_IdtfAllowEmpty), "Mode",            StdCopyStrBuf()));
233 	pComp->Value(mkNamingAdapt(Goals,                                             "Goals",           C4IDList()));
234 	pComp->Value(mkNamingAdapt(Rules,                                             "Rules",           C4IDList()));
235 	pComp->Value(mkNamingAdapt(FoWEnabled,                                        "FoWEnabled",      true));
236 	pComp->Value(mkNamingAdapt(EvaluateOnAbort,                                   "EvaluateOnAbort", false));
237 }
238 
Default()239 void C4SPlrStart::Default()
240 {
241 	Wealth.Set(0,0,0,250);
242 	Position[0]=Position[1]=-1;
243 	EnforcePosition=0;
244 	ReadyCrew.Default();
245 	ReadyBase.Default();
246 	ReadyVehic.Default();
247 	ReadyMaterial.Default();
248 	BuildKnowledge.Default();
249 	BaseMaterial.Default();
250 	BaseProduction.Default();
251 }
252 
EquipmentEqual(C4SPlrStart & rhs)253 bool C4SPlrStart::EquipmentEqual(C4SPlrStart &rhs)
254 {
255 	return *this == rhs;
256 }
257 
operator ==(const C4SPlrStart & rhs)258 bool C4SPlrStart::operator==(const C4SPlrStart& rhs)
259 {
260 	return (Wealth == rhs.Wealth)
261 	       && (ReadyCrew == rhs.ReadyCrew)
262 	       && (ReadyBase == rhs.ReadyBase)
263 	       && (ReadyVehic == rhs.ReadyVehic)
264 	       && (ReadyMaterial == rhs.ReadyMaterial)
265 	       && (BuildKnowledge == rhs.BuildKnowledge)
266 	       && (BaseMaterial == rhs.BaseMaterial)
267 	       && (BaseProduction == rhs.BaseProduction);
268 }
269 
CompileFunc(StdCompiler * pComp)270 void C4SPlrStart::CompileFunc(StdCompiler *pComp)
271 {
272 	C4IDList crewDefault;
273 	crewDefault.SetIDCount(C4ID::Clonk,1,true);
274 	pComp->Value(mkNamingAdapt(Wealth,                  "Wealth",                C4SVal(0, 0, 0,250), true));
275 	pComp->Value(mkNamingAdapt(mkArrayAdaptDM(Position,-1), "Position"           ));
276 	pComp->Value(mkNamingAdapt(EnforcePosition,         "EnforcePosition",       0));
277 	pComp->Value(mkNamingAdapt(ReadyCrew,               "Crew",                  crewDefault));
278 	pComp->Value(mkNamingAdapt(ReadyBase,               "Buildings",             C4IDList()));
279 	pComp->Value(mkNamingAdapt(ReadyVehic,              "Vehicles",              C4IDList()));
280 	pComp->Value(mkNamingAdapt(ReadyMaterial,           "Material",              C4IDList()));
281 	pComp->Value(mkNamingAdapt(BuildKnowledge,          "Knowledge",             C4IDList()));
282 	pComp->Value(mkNamingAdapt(BaseMaterial,        "BaseMaterial",      C4IDList()));
283 	pComp->Value(mkNamingAdapt(BaseProduction,      "BaseProduction",    C4IDList()));
284 }
285 
Default()286 void C4SLandscape::Default()
287 {
288 	BottomOpen=0; TopOpen=1;
289 	LeftOpen=0; RightOpen=0;
290 	AutoScanSideOpen=1;
291 	SkyDef[0]=0;
292 	for (int & cnt : SkyDefFade) cnt=0;
293 	VegLevel.Set(50,30,0,100);
294 	Vegetation.Default();
295 	InEarthLevel.Set(50,0,0,100);
296 	InEarth.Default();
297 	MapWdt.Set(100,0,64,250);
298 	MapHgt.Set(50,0,40,250);
299 	MapZoom.Set(8,0,1,15);
300 	Amplitude.Set(0,0);
301 	Phase.Set(50);
302 	Period.Set(15);
303 	Random.Set(0);
304 	LiquidLevel.Default();
305 	MapPlayerExtend=0;
306 	Layers.Clear();
307 	Material = "Earth";
308 	Liquid = "Water";
309 	ExactLandscape=false;
310 	Gravity.Set(100,0,10,200);
311 	NoScan=false;
312 	KeepMapCreator=false;
313 	SkyScrollMode=0;
314 	MaterialZoom=4;
315 	FlatChunkShapes=false;
316 	Secret=false;
317 }
318 
GetMapSize(int32_t & rWdt,int32_t & rHgt,int32_t iPlayerNum)319 void C4SLandscape::GetMapSize(int32_t &rWdt, int32_t &rHgt, int32_t iPlayerNum)
320 {
321 	rWdt = MapWdt.Evaluate();
322 	rHgt = MapHgt.Evaluate();
323 	iPlayerNum = std::max<int32_t>( iPlayerNum, 1 );
324 	if (MapPlayerExtend)
325 		rWdt = std::min(rWdt * std::min(iPlayerNum, C4S_MaxMapPlayerExtend), MapWdt.Max);
326 }
327 
CompileFunc(StdCompiler * pComp)328 void C4SLandscape::CompileFunc(StdCompiler *pComp)
329 {
330 	pComp->Value(mkNamingAdapt(ExactLandscape,          "ExactLandscape",        false));
331 	pComp->Value(mkNamingAdapt(Vegetation,              "Vegetation",            C4IDList()));
332 	pComp->Value(mkNamingAdapt(VegLevel,                "VegetationLevel",       C4SVal(50,30,0,100), true));
333 	pComp->Value(mkNamingAdapt(InEarth,                 "InEarth",               C4IDList()));
334 	pComp->Value(mkNamingAdapt(InEarthLevel,            "InEarthLevel",          C4SVal(50,0,0,100), true));
335 	pComp->Value(mkNamingAdapt(mkStringAdaptA(SkyDef),  "Sky",                   ""));
336 	pComp->Value(mkNamingAdapt(mkArrayAdaptDM(SkyDefFade,0),"SkyFade"            ));
337 	pComp->Value(mkNamingAdapt(BottomOpen,              "BottomOpen",            0));
338 	pComp->Value(mkNamingAdapt(TopOpen,                 "TopOpen",               1));
339 	pComp->Value(mkNamingAdapt(LeftOpen,                "LeftOpen",              0));
340 	pComp->Value(mkNamingAdapt(RightOpen,               "RightOpen",             0));
341 	pComp->Value(mkNamingAdapt(AutoScanSideOpen,        "AutoScanSideOpen",      1));
342 	pComp->Value(mkNamingAdapt(MapWdt,                  "MapWidth",              C4SVal(100,0,64,250), true));
343 	pComp->Value(mkNamingAdapt(MapHgt,                  "MapHeight",             C4SVal(50,0,40,250), true));
344 	pComp->Value(mkNamingAdapt(MapZoom,                 "MapZoom",               C4SVal(8,0,1,15), true));
345 	pComp->Value(mkNamingAdapt(Amplitude,               "Amplitude",             C4SVal(0)));
346 	pComp->Value(mkNamingAdapt(Phase,                   "Phase",                 C4SVal(50)));
347 	pComp->Value(mkNamingAdapt(Period,                  "Period",                C4SVal(15)));
348 	pComp->Value(mkNamingAdapt(Random,                  "Random",                C4SVal(0)));
349 	pComp->Value(mkNamingAdapt(mkStringAdaptA(Material),"Material",              "Earth"));
350 	pComp->Value(mkNamingAdapt(mkStringAdaptA(Liquid),  "Liquid",                "Water"));
351 	pComp->Value(mkNamingAdapt(LiquidLevel,             "LiquidLevel",           C4SVal()));
352 	pComp->Value(mkNamingAdapt(MapPlayerExtend,         "MapPlayerExtend",       0));
353 	pComp->Value(mkNamingAdapt(Layers,                  "Layers",                C4NameList()));
354 	pComp->Value(mkNamingAdapt(Gravity,                 "Gravity",               C4SVal(100,0,10,200), true));
355 	pComp->Value(mkNamingAdapt(NoScan,                  "NoScan",                false));
356 	pComp->Value(mkNamingAdapt(KeepMapCreator,          "KeepMapCreator",        false));
357 	pComp->Value(mkNamingAdapt(SkyScrollMode,           "SkyScrollMode",         0));
358 	pComp->Value(mkNamingAdapt(MaterialZoom,            "MaterialZoom",          4));
359 	pComp->Value(mkNamingAdapt(FlatChunkShapes,         "FlatChunkShapes",       false));
360 	pComp->Value(mkNamingAdapt(Secret,                  "Secret",                false));
361 }
362 
Default()363 void C4SWeather::Default()
364 {
365 	Climate.Set(50,10);
366 	StartSeason.Set(50,50);
367 	YearSpeed.Set(50);
368 	Wind.Set(0,70,-100,+100);
369 	NoGamma=true;
370 }
371 
CompileFunc(StdCompiler * pComp)372 void C4SWeather::CompileFunc(StdCompiler *pComp)
373 {
374 	pComp->Value(mkNamingAdapt(Climate,                 "Climate",               C4SVal(50,10), true));
375 	pComp->Value(mkNamingAdapt(StartSeason,             "StartSeason",           C4SVal(50,50), true));
376 	pComp->Value(mkNamingAdapt(YearSpeed,               "YearSpeed",               C4SVal(50)));
377 	pComp->Value(mkNamingAdapt(Wind,                    "Wind",                  C4SVal(0,70,-100,+100), true));
378 	pComp->Value(mkNamingAdapt(NoGamma,                 "NoGamma",               true));
379 }
380 
Default()381 void C4SAnimals::Default()
382 {
383 	FreeLife.Clear();
384 	EarthNest.Clear();
385 }
386 
CompileFunc(StdCompiler * pComp)387 void C4SAnimals::CompileFunc(StdCompiler *pComp)
388 {
389 	pComp->Value(mkNamingAdapt(FreeLife,                "Animal",               C4IDList()));
390 	pComp->Value(mkNamingAdapt(EarthNest,               "Nest",                  C4IDList()));
391 }
392 
Default()393 void C4SEnvironment::Default()
394 {
395 	Objects.Clear();
396 }
397 
CompileFunc(StdCompiler * pComp)398 void C4SEnvironment::CompileFunc(StdCompiler *pComp)
399 {
400 	pComp->Value(mkNamingAdapt(Objects,                 "Objects",               C4IDList()));
401 }
402 
Default()403 void C4SRealism::Default()
404 {
405 	LandscapePushPull=false;
406 	LandscapeInsertThrust=false;
407 	ValueOverloads.Default();
408 }
409 
Clear()410 void C4Scenario::Clear()
411 {
412 
413 }
414 
SetExactLandscape()415 void C4Scenario::SetExactLandscape()
416 {
417 	if (Landscape.ExactLandscape) return;
418 	// Set landscape
419 	Landscape.ExactLandscape = true;
420 }
421 
GetModules(StdStrBuf * psOutModules) const422 bool C4SDefinitions::GetModules(StdStrBuf *psOutModules) const
423 {
424 	// Local only
425 	if (LocalOnly) { psOutModules->Copy(""); return true; }
426 	// Scan for any valid entries
427 	bool fSpecified = false;
428 	int32_t cnt=0;
429 	for (; cnt<C4S_MaxDefinitions; cnt++)
430 		if (Definition[cnt][0])
431 			fSpecified = true;
432 	// No valid entries
433 	if (!fSpecified) return false;
434 	// Compose entry list
435 	psOutModules->Copy("");
436 	for (cnt=0; cnt<C4S_MaxDefinitions; cnt++)
437 		if (Definition[cnt][0])
438 		{
439 			if (psOutModules->getLength()) psOutModules->AppendChar(';');
440 			psOutModules->Append(Definition[cnt]);
441 		}
442 	// Done
443 	return true;
444 }
445 
GetModulesAsList() const446 std::list<const char *> C4SDefinitions::GetModulesAsList() const
447 {
448 	// get definitions as string pointers into this structure
449 	std::list<const char *> result;
450 	if (!LocalOnly)
451 	{
452 		for (const char *def : Definition)
453 		{
454 			if (*def)
455 			{
456 				result.push_back(def);
457 			}
458 		}
459 	}
460 	return result;
461 }
462 
463 
SetModules(const char * szList,const char * szRelativeToPath,const char * szRelativeToPath2)464 void C4SDefinitions::SetModules(const char *szList, const char *szRelativeToPath, const char *szRelativeToPath2)
465 {
466 	int32_t cnt;
467 
468 	// Empty list: local only
469 	if (!SModuleCount(szList))
470 	{
471 		LocalOnly=true;
472 		for (cnt=0; cnt<C4S_MaxDefinitions; cnt++) Definition[cnt][0]=0;
473 		return;
474 	}
475 
476 	// Set list
477 	LocalOnly=false;
478 	for (cnt=0; cnt<C4S_MaxDefinitions; cnt++)
479 	{
480 		SGetModule(szList,cnt,Definition[cnt],_MAX_PATH);
481 		// Make relative path
482 		if (szRelativeToPath && *szRelativeToPath)
483 		{
484 			if (GetRelativePathS(Definition[cnt],szRelativeToPath) != Definition[cnt])
485 			{
486 				SCopy(GetRelativePathS(Definition[cnt],szRelativeToPath),Definition[cnt]);
487 				continue;
488 			}
489 		}
490 		if (szRelativeToPath2 && *szRelativeToPath2)
491 		{
492 			if (GetRelativePathS(Definition[cnt],szRelativeToPath2) != Definition[cnt])
493 			{
494 				SCopy(GetRelativePathS(Definition[cnt],szRelativeToPath2),Definition[cnt]);
495 				continue;
496 			}
497 		}
498 	}
499 
500 }
501 
CompileFunc(StdCompiler * pComp)502 void C4SDefinitions::CompileFunc(StdCompiler *pComp)
503 {
504 	pComp->Value(mkNamingAdapt(LocalOnly,               "LocalOnly",             false));
505 	pComp->Value(mkNamingAdapt(AllowUserChange,         "AllowUserChange",       false));
506 	pComp->Value(mkNamingAdapt(mkStringAdaptMA(Definition[0]), "Definition1", "Objects.ocd"));
507 	for (int32_t i = 1; i < C4S_MaxDefinitions; i++)
508 		pComp->Value(mkNamingAdapt(mkStringAdaptMA(Definition[i]), FormatString("Definition%i", i+1).getData(), ""));
509 	pComp->Value(mkNamingAdapt(SkipDefs,                "SkipDefs",              C4IDList()));
510 }
511 
IsMelee()512 bool C4SGame::IsMelee()
513 {
514 	// Check for game modes known to be melees
515 	// Also allow it in parkours by default because that works fine
516 	if (Mode == "Melee" || Mode == "Parkour") return true;
517 	// Game mode not present or unknown? Check for old MELE goal which was still used by some scenarios.
518 	if (Goals.GetIDCount(C4ID::Melee, 1)) return true;
519 	// Nothing looks like melee here
520 	return false;
521 }
522