1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-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 #include "C4Include.h"
17 #include "player/C4ScenarioParameters.h"
18 #include "c4group/C4Components.h"
19 #include "script/C4Aul.h"
20 
21 // *** C4ScenarioParameters
22 
CompileFunc(StdCompiler * pComp)23 void C4ScenarioParameterDef::Option::CompileFunc(StdCompiler *pComp)
24 {
25 	if (!pComp->Name("Option")) { pComp->NameEnd(); pComp->excNotFound("Option"); }
26 	pComp->Value(mkNamingAdapt(mkParAdapt(Name, StdCompiler::RCT_All),          "Name",         StdCopyStrBuf()));
27 	pComp->Value(mkNamingAdapt(mkParAdapt(Description, StdCompiler::RCT_All),   "Description",  StdCopyStrBuf()));
28 	pComp->Value(mkNamingAdapt(           Value,                                "Value",        0));
29 	pComp->NameEnd();
30 }
31 
GetOptionByValue(int32_t val) const32 const C4ScenarioParameterDef::Option *C4ScenarioParameterDef::GetOptionByValue(int32_t val) const
33 {
34 	// search option by value
35 	for (const auto & Option : Options)
36 		if (Option.Value == val)
37 			return &Option;
38 	return nullptr;
39 }
40 
GetOptionByIndex(size_t idx) const41 const C4ScenarioParameterDef::Option *C4ScenarioParameterDef::GetOptionByIndex(size_t idx) const
42 {
43 	if (idx >= Options.size()) return nullptr;
44 	return &Options[idx];
45 }
46 
CompileFunc(StdCompiler * pComp)47 void C4ScenarioParameterDef::CompileFunc(StdCompiler *pComp)
48 {
49 	if (!pComp->Name("ParameterDef")) { pComp->NameEnd(); pComp->excNotFound("ParameterDef"); }
50 	pComp->Value(mkNamingAdapt(mkParAdapt(Name, StdCompiler::RCT_All),          "Name",         StdCopyStrBuf()));
51 	pComp->Value(mkNamingAdapt(mkParAdapt(Description, StdCompiler::RCT_All),   "Description",  StdCopyStrBuf()));
52 	pComp->Value(mkNamingAdapt(mkParAdapt(ID, StdCompiler::RCT_Idtf),           "ID",           StdCopyStrBuf()));
53 	StdEnumEntry<ParameterType> ParTypeEntries[] =
54 	{
55 		{ "Enumeration", SPDT_Enum },
56 		{ nullptr, SPDT_Enum }
57 	};
58 	pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(Type, ParTypeEntries),         "Type",         SPDT_Enum));
59 	pComp->Value(mkNamingAdapt(Default,                                             "Default",      0));
60 	pComp->Value(mkNamingAdapt(LeagueValue,                                         "LeagueValue",  0));
61 	pComp->Value(mkNamingAdapt(mkParAdapt(Achievement, StdCompiler::RCT_Idtf),      "Achievement",  StdCopyStrBuf()));
62 	pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(Options, StdCompiler::SEP_NONE), "Options"));
63 	pComp->NameEnd();
64 }
65 
CompileFunc(StdCompiler * pComp)66 void C4ScenarioParameterDefs::CompileFunc(StdCompiler *pComp)
67 {
68 	pComp->Value(mkSTLContainerAdapt(Parameters, StdCompiler::SEP_NONE));
69 }
70 
GetParameterDefByIndex(size_t idx) const71 const C4ScenarioParameterDef *C4ScenarioParameterDefs::GetParameterDefByIndex(size_t idx) const
72 {
73 	if (idx >= Parameters.size()) return nullptr;
74 	return &Parameters[idx];
75 }
76 
Load(C4Group & hGroup,C4LangStringTable * pLang)77 bool C4ScenarioParameterDefs::Load(C4Group &hGroup, C4LangStringTable *pLang)
78 {
79 	// Load buffer, localize and parse
80 	StdStrBuf Buf;
81 	if (!hGroup.LoadEntryString(C4CFN_ScenarioParameterDefs,&Buf)) return false;
82 	if (pLang) pLang->ReplaceStrings(Buf);
83 	if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Buf, C4CFN_ScenarioParameterDefs))
84 		{ return false; }
85 	return true;
86 }
87 
RegisterScriptConstants(const C4ScenarioParameters & values)88 void C4ScenarioParameterDefs::RegisterScriptConstants(const C4ScenarioParameters &values)
89 {
90 	// register constants for all parameters in script engine
91 
92 	// old-style: one constant per parameter
93 	for (const auto & Parameter : Parameters)
94 	{
95 		StdStrBuf constant_name;
96 		constant_name.Format("SCENPAR_%s", Parameter.GetID());
97 		int32_t constant_value = values.GetValueByID(Parameter.GetID(), Parameter.GetDefault());
98 		::ScriptEngine.RegisterGlobalConstant(constant_name.getData(), C4VInt(constant_value));
99 	}
100 
101 	// new-style: all constants in a proplist
102 	auto scenpar = C4PropList::NewStatic(nullptr, nullptr, &Strings.P[P_SCENPAR]);
103 	for (const auto & Parameter : Parameters)
104 	{
105 		int32_t constant_value = values.GetValueByID(Parameter.GetID(), Parameter.GetDefault());
106 		scenpar->SetPropertyByS(Strings.RegString(StdStrBuf(Parameter.GetID())), C4VInt(constant_value));
107 	}
108 	scenpar->Freeze();
109 	::ScriptEngine.RegisterGlobalConstant("SCENPAR", C4Value(scenpar));
110 }
111 
Clear()112 void C4ScenarioParameters::Clear()
113 {
114 	Parameters.clear();
115 }
116 
Merge(const C4ScenarioParameters & other)117 void C4ScenarioParameters::Merge(const C4ScenarioParameters &other)
118 {
119 	// Merge lists and keep larger value
120 	for (const auto & Parameter : other.Parameters)
121 	{
122 		auto j = Parameters.find(Parameter.first);
123 		if (j != Parameters.end())
124 			if (j->second >= Parameter.second)
125 				continue; // existing value is same or larger - keep old
126 		// update to new value from other list
127 		Parameters[Parameter.first] = Parameter.second;
128 	}
129 }
130 
GetValueByID(const char * id,int32_t default_value) const131 int32_t C4ScenarioParameters::GetValueByID(const char *id, int32_t default_value) const
132 {
133 	// return map value if name is in map. Otherwise, return default value.
134 	auto i = Parameters.find(StdStrBuf(id));
135 	if (i != Parameters.end()) return i->second; else return default_value;
136 }
137 
SetValue(const char * id,int32_t value,bool only_if_larger)138 void C4ScenarioParameters::SetValue(const char *id, int32_t value, bool only_if_larger)
139 {
140 	if (only_if_larger)
141 	{
142 		auto i = Parameters.find(StdStrBuf(id));
143 		if (i != Parameters.end())
144 			if (i->second >= value)
145 				// could become smaller. don't set.
146 				return;
147 	}
148 	// just update map
149 	Parameters[StdCopyStrBuf(id)] = value;
150 }
151 
CompileFunc(StdCompiler * pComp)152 void C4ScenarioParameters::CompileFunc(StdCompiler *pComp)
153 {
154 	// Unfortunately, StdCompiler cannot save std::map yet
155 	if (pComp->isDeserializer())
156 	{
157 		Parameters.clear();
158 		if (pComp->hasNaming())
159 		{
160 			// load from INI
161 			size_t name_count = pComp->NameCount();
162 			for (size_t i=0; i<name_count; ++i)
163 			{
164 				int32_t v=0;
165 				const char *name = pComp->GetNameByIndex(0); // always get name index 0, because names are removed after values have been extracted for them
166 				StdCopyStrBuf sName(name);
167 				if (!name) continue;
168 				pComp->Value(mkNamingAdapt(v, sName.getData(), 0));
169 				Parameters[sName] = v;
170 			}
171 		}
172 		else
173 		{
174 			// load from binary
175 			int32_t name_count=0;
176 			pComp->Value(name_count);
177 			for (int32_t i=0; i<name_count; ++i)
178 			{
179 				StdCopyStrBuf name; int32_t v;
180 				pComp->Value(name);
181 				pComp->Value(v);
182 				Parameters[name] = v;
183 			}
184 		}
185 	}
186 	else
187 	{
188 		if (pComp->hasNaming())
189 		{
190 			// save to INI
191 			for (auto & Parameter : Parameters)
192 				pComp->Value(mkNamingAdapt(Parameter.second, Parameter.first.getData()));
193 		}
194 		else
195 		{
196 			// save to binary
197 			int32_t name_count=Parameters.size();
198 			pComp->Value(name_count);
199 			for (auto & Parameter : Parameters)
200 			{
201 				pComp->Value(const_cast<StdCopyStrBuf &>(Parameter.first));
202 				pComp->Value(Parameter.second);
203 			}
204 		}
205 	}
206 }
207 
AddFilename2ID(const char * filename,const char * id)208 StdStrBuf C4ScenarioParameters::AddFilename2ID(const char *filename, const char *id)
209 {
210 	// composes an ID string that contains both the relevant part of the filename and the ID
211 	// we care for .oc* folders only
212 	StdStrBuf sResult, sSource(filename, true), sPart;
213 	sSource.ReplaceChar(AltDirectorySeparator, DirectorySeparator);
214 	size_t idx=0;
215 	while (sSource.GetSection(idx++, &sPart, DirectorySeparator))
216 	{
217 		size_t len = sPart.getLength();
218 		if (len > 4 && SEqual2NoCase(sPart.getPtr(len - 4), ".oc", 3))
219 		{
220 			// .oc* folders separated by underscores
221 			sResult.Append(sPart.getData(), len - 4);
222 			sResult.AppendChar('_');
223 		}
224 	}
225 	sResult.Append(id);
226 	return sResult;
227 }
228