1 #include "pch.h"
2 #include "common/Def_Str.h"
3 #include "common/data/CData.h"
4 #include "common/data/TracksXml.h"
5 #include "common/GuiCom.h"
6 #include "common/CScene.h"
7 #include "../vdrift/pathmanager.h"
8 #include "../vdrift/game.h"
9 #include "../road/Road.h"
10 #include "CGame.h"
11 #include "CHud.h"
12 #include "CGui.h"
13 #include "common/MultiList2.h"
14 #include "../sound/SoundMgr.h"
15 #include <OgreTextureManager.h>
16 using namespace std;
17 using namespace Ogre;
18 using namespace MyGUI;
19 
20 
21 ///
BackFromChs()22 void CGui::BackFromChs()
23 {
24 	pSet->gui.champ_num = -1;
25 	pSet->gui.chall_num = -1;
26 	//pChall = 0;
27 	//CarListUpd();  // off filtering
28 }
29 
isChallGui()30 bool CGui::isChallGui()
31 {
32 	//return imgChall && imgChall->getVisible();
33 	return pSet->inMenu == MNU_Challenge;
34 }
35 
tabChallType(Tab wp,size_t id)36 void CGui::tabChallType(Tab wp, size_t id)
37 {
38 	pSet->chall_type = id;
39 	ChallsListUpdate();
40 }
41 
42 
43 ///  Challenges list  fill
44 //----------------------------------------------------------------------------------------------------------------------
ChallsListUpdate()45 void CGui::ChallsListUpdate()
46 {
47 	const char clrCh[8][8] = {
48 	//  0 Rally  1 Scenery  2 Endurance  3 Chase  4 Stunts  5 Extreme  6 Special  7 Test
49 		"#A0D0FF","#80FF80","#C0FF60","#FFC060","#FF8080","#C0A0E0", "#60B0FF","#909090" };
50 
51 	liChalls->removeAllItems();  int n=1;  size_t sel = ITEM_NONE;
52 	int p = pSet->gui.champ_rev ? 1 : 0;
53 	for (int i=0; i < data->chall->all.size(); ++i,++n)
54 	{
55 		const Chall& chl = data->chall->all[i];
56 		if (chl.type == pSet->chall_type)
57 		{
58 			const ProgressChall& pc = progressL[p].chs[i];
59 			int ntrks = pc.trks.size(), ct = pc.curTrack;
60 			const String& clr = clrCh[chl.type];
61 			//String cars = data->carsXml.colormap[chl.ci->type];  if (cars.length() != 7)  clr = "#C0D0E0";
62 
63 			liChalls->addItem(""/*clr+ toStr(n/10)+toStr(n%10)*/, n);  int l = liChalls->getItemCount()-1;
64 			liChalls->setSubItemNameAt(1,l, clr+ chl.name.c_str());
65 			liChalls->setSubItemNameAt(2,l, gcom->clrsDiff[chl.diff]+ TR("#{Diff"+toStr(chl.diff)+"}"));
66 			liChalls->setSubItemNameAt(3,l, StrChallCars(chl));
67 
68 			liChalls->setSubItemNameAt(4,l, gcom->clrsDiff[std::min(8,ntrks*2/3+1)]+ iToStr(ntrks,3));
69 			liChalls->setSubItemNameAt(5,l, gcom->clrsDiff[std::min(8,int(chl.time/3.f/60.f))]+ StrTime2(chl.time));
70 			liChalls->setSubItemNameAt(6,l, ct == 0 || ct == ntrks ? "" :
71 				clr+ fToStr(100.f * ct / ntrks,0,3)+" %");
72 
73 			liChalls->setSubItemNameAt(7,l, " "+ StrPrize(pc.fin+1));
74 			liChalls->setSubItemNameAt(8,l, clr+ (pc.fin >= 0 ? fToStr(pc.avgPoints,1,5) : ""));
75 			if (n-1 == pSet->gui.chall_num)  sel = l;
76 	}	}
77 	liChalls->setIndexSelected(sel);
78 }
79 
80 
81 ///  Challenges list  sel changed,  fill Stages list
82 //----------------------------------------------------------------------------------------------------------------------
listChallChng(MyGUI::MultiList2 * chlist,size_t id)83 void CGui::listChallChng(MyGUI::MultiList2* chlist, size_t id)
84 {
85 	if (id==ITEM_NONE || liChalls->getItemCount() == 0)  return;
86 
87 	int nch = *liChalls->getItemDataAt<int>(id)-1;
88 	if (nch < 0 || nch >= data->chall->all.size())  {  LogO("Error chall sel > size.");  return;  }
89 
90 	CarListUpd();  // filter car list
91 
92 	//  fill stages
93 	liStages->removeAllItems();
94 
95 	int n = 1, p = pSet->gui.champ_rev ? 1 : 0;
96 	const Chall& ch = data->chall->all[nch];
97 	int ntrks = ch.trks.size();
98 	for (int i=0; i < ntrks; ++i,++n)
99 	{
100 		const ChallTrack& trk = ch.trks[i];
101 		float po = progressL[p].chs[nch].trks[i].points;
102 		StageListAdd(n, trk.name, trk.laps, po > 0.f ? "#E0F0FF"+fToStr(po,1,3) : "");
103 	}
104 	if (edChDesc)  edChDesc->setCaption(ch.descr);
105 
106 	UpdChallDetail(nch);
107 }
108 
109 
110 //  list allowed cars types and cars
StrChallCars(const Chall & ch)111 String CGui::StrChallCars(const Chall& ch)
112 {
113 	String str;
114 	int i,s;
115 
116 	s = ch.carTypes.size();
117 	for (i=0; i < s; ++i)
118 	{
119 		const String& ct = ch.carTypes[i];
120 			str += data->cars->colormap[ct];  // car type color
121 		str += ct;
122 		if (i+1 < s)  str += ",";
123 	}
124 	//int id = data->carsXml.carmap[*i];
125 	//data->carsXml.cars[id-1];
126 	if (!str.empty())
127 		str += " ";
128 
129 	s = ch.cars.size();
130 	for (i=0; i < s; ++i)
131 	{
132 		const String& c = ch.cars[i];
133 			int id = data->cars->carmap[c]-1;  // get car color from type
134 			if (id >= 0)  str += data->cars->colormap[ data->cars->cars[id].type ];
135 		str += c;
136 		if (i+1 < s)  str += ",";
137 	}
138 	return str;
139 }
140 
141 //  test if car is in challenge allowed cars or types
IsChallCar(String name)142 bool CGui::IsChallCar(String name)
143 {
144 	if (!liChalls || liChalls->getIndexSelected()==ITEM_NONE)  return true;
145 
146 	int chId = *liChalls->getItemDataAt<int>(liChalls->getIndexSelected())-1;
147 	const Chall& ch = data->chall->all[chId];
148 
149 	int i,s;
150 	if (!ch.carTypes.empty())
151 	{	s = ch.carTypes.size();
152 
153 		int id = data->cars->carmap[name]-1;
154 		if (id >= 0)
155 		{
156 			String type = data->cars->cars[id].type;
157 
158 			for (i=0; i < s; ++i)
159 				if (type == ch.carTypes[i])  return true;
160 	}	}
161 	if (!ch.cars.empty())
162 	{	s = ch.cars.size();
163 
164 		for (i=0; i < s; ++i)
165 			if (name == ch.cars[i])  return true;
166 	}
167 	return false;
168 }
169 
170 
171 ///  chall start
172 //---------------------------------------------------------------------
btnChallStart(WP)173 void CGui::btnChallStart(WP)
174 {
175 	if (liChalls->getIndexSelected()==ITEM_NONE)  return;
176 	pSet->gui.champ_num = -1;
177 	pSet->gui.chall_num = *liChalls->getItemDataAt<int>(liChalls->getIndexSelected())-1;
178 
179 	//  if already finished, restart - will loose progress and scores ..
180 	int chId = pSet->gui.chall_num, p = pSet->game.champ_rev ? 1 : 0;
181 	LogO("|] Starting chall: "+toStr(chId)+(p?" rev":""));
182 	ProgressChall& pc = progressL[p].chs[chId];
183 	if (pc.curTrack == pc.trks.size())
184 	{
185 		LogO("|] Was at 100%, restarting progress.");
186 		pc.curTrack = 0;  //pc.score = 0.f;
187 	}
188 	// change btn caption to start/continue/restart ?..
189 
190 	btnNewGame(0);
191 }
192 
193 ///  stage start / end
194 //----------------------------------------------------------------------------------------------------------------------
btnChallStageStart(WP)195 void CGui::btnChallStageStart(WP)
196 {
197 	//  check if chall ended
198 	int chId = pSet->game.chall_num, p = pSet->game.champ_rev ? 1 : 0;
199 	ProgressChall& pc = progressL[p].chs[chId];
200 	const Chall& ch = data->chall->all[chId];
201 	bool last = pc.curTrack == ch.trks.size();
202 
203 	LogO("|] This was stage " + toStr(pc.curTrack) + "/" + toStr(ch.trks.size()) + " btn");
204 	if (last)
205 	{	//  show end window, todo: start particles..
206 		app->mWndChallStage->setVisible(false);
207 		// tutorial, tutorial hard, normal, hard, very hard, scenery, test
208 		const int ui[8] = {0,1,2,3,4,5,0,0};
209 		//if (imgChallEnd)
210 		//	imgChallEnd->setImageCoord(IntCoord(ui[std::min(7, std::max(0, ch.type))]*128,0,128,256));
211 		app->mWndChallEnd->setVisible(true);
212 
213 		///  sounds  //)
214 		if (iChSnd < 0)
215 			pGame->snd_fail->start();
216 		else
217 			pGame->snd_win[iChSnd]->start();  //)
218 		return;
219 	}
220 
221 	bool finished = pGame->timer.GetLastLap(0) > 0.f;  //?-
222 	if (finished)
223 	{
224 		LogO("|] Loading next stage.");
225 		app->mWndChallStage->setVisible(false);
226 		btnNewGame(0);
227 	}else
228 	{
229 		LogO("|] Starting stage.");
230 		app->mWndChallStage->setVisible(false);
231 		pGame->pause = false;
232 		pGame->timer.waiting = false;
233 		pGame->timer.end_sim = false;
234 	}
235 }
236 
237 //  stage back
btnChallStageBack(WP)238 void CGui::btnChallStageBack(WP)
239 {
240 	app->mWndChallStage->setVisible(false);
241 	app->isFocGui = true;  // show back gui
242 	toggleGui(false);
243 }
244 
245 //  chall end
btnChallEndClose(WP)246 void CGui::btnChallEndClose(WP)
247 {
248 	app->mWndChallEnd->setVisible(false);
249 }
250 
251 
252 ///  save progressL and update it on gui
ProgressLSave(bool upgGui)253 void CGui::ProgressLSave(bool upgGui)
254 {
255 	progressL[0].SaveXml(PATHMANAGER::UserConfigDir() + "/progressL.xml");
256 	progressL[1].SaveXml(PATHMANAGER::UserConfigDir() + "/progressL_rev.xml");
257 	if (!upgGui)
258 		return;
259 	ChallsListUpdate();
260 	listChallChng(liChalls, liChalls->getIndexSelected());
261 }
262 
263 
264 ///  challenge advance logic
265 //  caution: called from GAME, 2nd thread, no Ogre stuff here
266 //----------------------------------------------------------------------------------------------------------------------
ChallengeAdvance(float timeCur)267 void CGui::ChallengeAdvance(float timeCur/*total*/)
268 {
269 	int chId = pSet->game.chall_num, p = pSet->game.champ_rev ? 1 : 0;
270 	ProgressChall& pc = progressL[p].chs[chId];
271 	ProgressTrackL& pt = pc.trks[pc.curTrack];
272 	const Chall& ch = data->chall->all[chId];
273 	const ChallTrack& trk = ch.trks[pc.curTrack];
274 	LogO("|] --- Chall end: " + ch.name);
275 
276 	///  compute track  poins  --------------
277 	float timeTrk = data->tracks->times[trk.name];
278 	if (timeTrk < 1.f)
279 	{	LogO("|] Error: Track has no best time !");  timeTrk = 10.f;	}
280 	timeTrk *= trk.laps;
281 
282 	LogO("|] Track: " + trk.name);
283 	LogO("|] Your time: " + toStr(timeCur));
284 	LogO("|] Best time: " + toStr(timeTrk));
285 
286 	float carMul = app->GetCarTimeMul(pSet->game.car[0], pSet->game.sim_mode);
287 	float points = 0.f;  int pos = 0;
288 
289 	#if 1  // test score +- sec diff
290 	for (int i=-2; i <= 2; ++i)
291 	{
292 		pos = app->GetRacePos(timeCur + i*2.f, timeTrk, carMul, true, &points);
293 		LogO("|] var, add time: "+toStr(i*2)+" sec, points: "+fToStr(points,2));
294 	}
295 	#endif
296 	pos = app->GetRacePos(timeCur, timeTrk, carMul, true, &points);
297 
298 	pt.time = timeCur;  pt.points = points;  pt.pos = pos;
299 
300 
301 	///  Pass Stage  --------------
302 	bool passed = true, pa;
303 	if (trk.timeNeeded > 0.f)
304 	{
305 		pa = pt.time <= trk.timeNeeded;
306 		LogO("]] TotTime: " + StrTime(pt.time) + "  Needed: " + StrTime(trk.timeNeeded) + "  Passed: " + (pa ? "yes":"no"));
307 		passed &= pa;
308 	}
309 	if (trk.passPoints > 0.f)
310 	{
311 		pa = pt.points >= trk.passPoints;
312 		LogO("]] Points: " + fToStr(pt.points,1) + "  Needed: " + fToStr(trk.passPoints,1) + "  Passed: " + (pa ? "yes":"no"));
313 		passed &= pa;
314 	}
315 	if (trk.passPos > 0)
316 	{
317 		pa = pt.pos <= trk.passPos;
318 		LogO("]] Pos: " + toStr(pt.pos) + "  Needed: " + toStr(trk.passPos) + "  Passed: " + (pa ? "yes":"no"));
319 		passed &= pa;
320 	}
321 	LogO(String("]] Passed total: ") + (passed ? "yes":"no"));
322 
323 
324 	//  --------------  advance  --------------
325 	bool last = pc.curTrack+1 == ch.trks.size();
326 	LogO("|] This was stage " + toStr(pc.curTrack+1) + "/" + toStr(ch.trks.size()));
327 
328 	pGame->pause = true;
329 	pGame->timer.waiting = true;
330 	pGame->timer.end_sim = true;
331 
332 	//  show stage end [window]
333 	ChallFillStageInfo(true);  // cur track
334 	app->mWndChallStage->setVisible(true);
335 
336 	//  sound  //)
337 	if (passed)
338 		pGame->snd_stage->start();
339 	else
340 		pGame->snd_fail->start();  //)
341 
342 
343 	if (!last || (last && !passed))
344 	{
345 		if (pc.curTrack == 0)  // save picked car
346 			pc.car = pSet->game.car[0];
347 
348 		if (passed)
349 			pc.curTrack++;  // next stage
350 
351 		ProgressLSave();
352 		return;
353 	}
354 
355 
356 	//  challenge end
357 	//-----------------------------------------------------------------------------------------------
358 
359 	//  compute avg
360 	float avgPos = 0.f, avgPoints = 0.f, totalTime = 0.f;
361 	int ntrks = pc.trks.size();
362 	for (int t=0; t < ntrks; ++t)
363 	{
364 		const ProgressTrackL& pt = pc.trks[t];
365 		totalTime += pt.time;  avgPoints += pt.points;  avgPos += pt.pos;
366 	}
367 	avgPoints /= ntrks;  avgPos /= ntrks;
368 
369 	//  save
370 	pc.curTrack++;
371 	pc.totalTime = totalTime;  pc.avgPoints = avgPoints;  pc.avgPos = avgPos;
372 
373 	LogO("|] Chall finished");
374 	String s = "\n\n"+
375 		TR("#C0D0F0#{Challenge}") + ": #C0D0FF" + ch.name +"\n";
376 	#define sPass(pa)  (pa ? TR("  #00FF00#{Passed}") : TR("  #FF8000#{DidntPass}"))
377 
378 
379 	///  Pass Challenge  --------------
380 	String ss;
381 	passed = true;
382 	int prize = -1, pp = ch.prizes;
383 
384 	//  time  1:00  ---------------
385 	if (ch.totalTime > 0.f)
386 	{
387 		pa = pc.totalTime <= ch.totalTime;
388 		// for p <= pp ..
389 
390 		LogO("]] TotalTime: "+StrTime(pc.totalTime)+"  Needed: "+StrTime(ch.totalTime)+"  Passed: "+(pa ? "yes":"no"));
391 		ss += TR("#D8C0FF#{TBTime}")+": "+StrTime(pc.totalTime)+"   /  "+StrTime(ch.totalTime) + sPass(pa) +"\n";
392 		passed &= pa;
393 	}
394 
395 	//  position  1st  ---------------
396 	if (ch.avgPos > 0.f)
397 	{
398 		ss += TR("#D8C0FF#{TBPosition}")+": "+fToStr(pc.avgPos,2,5)+"   /";
399 		String sp;  bool pa0;
400 		for (p=0; p <= pp; ++p)
401 		{
402 			float pass = ch.avgPos + ciAddPos[p] * ch.factor;
403 			pa = pc.avgPos <= pass;
404 			if (pa && p > prize)  prize = p;
405 
406 			LogO("]] AvgPos: "+fToStr(pc.avgPos,1)+"  Needed: "+fToStr(pass,1)+"  Prize: "+toStr(p)+"  Passed: "+(pa ? "yes":"no"));
407 			sp += "  "+clrPrize[2-pp+ p+1]+ fToStr(pass,1,3);
408 			if (p == 0)
409 			{	passed &= pa;  pa0 = pa;  }
410 		}
411 		ss += sp +" "+ sPass(pa0) +"\n";
412 	}
413 
414 	//  points  10.0  ---------------
415 	if (ch.avgPoints > 0.f)
416 	{
417 		ss += TR("#D8C0FF#{TBPoints}")+": "+fToStr(pc.avgPoints,2,5)+"   /";
418 		String sp;  bool pa0;
419 		for (p=0; p <= pp; ++p)
420 		{
421 			float pass = ch.avgPoints - cfSubPoints[p] * ch.factor;
422 			pa = pc.avgPoints >= pass;
423 			if (pa && p > prize)  prize = p;
424 
425 			LogO("]] AvgPoints: "+fToStr(pc.avgPoints,1)+"  Needed: "+fToStr(pass,1)+"  Prize: "+toStr(p)+"  Passed: "+(pa ? "yes":"no"));
426 			sp = "  "+clrPrize[2-pp+ p+1]+ fToStr(pass,1,3) + sp;
427 			if (p == 0)
428 			{	passed &= pa;  pa0 = pa;  }  // lost if didn't pass worst prize
429 		}
430 		ss += sp +" "+ sPass(pa0) +"\n";
431 	}else  //if (passed)  // write points always on end
432 		ss += TR("#C0E0FF#{TBPoints}")+": "+fToStr(pc.avgPoints,2,5) /*+ sPass(pa)*/ +"\n";
433 
434 	LogO(String("]] Passed total: ") + (passed ? "yes":"no") +"  Prize: "+toStr(prize));
435 
436 
437 	//  end
438 	pc.fin = passed ? prize : -1;
439 	if (passed)
440 		s += TR("\n#E0F0F8#{Prize}: ") + StrPrize(pc.fin+1)+"\n";
441 	s += "\n"+ss;
442 
443 	ProgressLSave();
444 
445 	//  save which sound to play  //)
446 	if (!passed)
447 		iChSnd = -1;
448 	else
449 		iChSnd = std::min(2, std::max(0, prize-1));  // ch.diff ?
450 
451 
452 	//  upd chall end [window]
453 	imgChallFail->setVisible(!passed);
454 	imgChallCup->setVisible( passed);  const int ui[8] = {2,3,4};
455 	imgChallCup->setImageCoord(IntCoord(ui[std::min(2, std::max(0, pc.fin))]*128,0,128,256));
456 
457 	txChallEndC->setCaption(passed ? TR("#{ChampEndCongrats}") : "");
458 	txChallEndF->setCaption(passed ? TR("#{ChallEndFinished}") : TR("#{Finished}"));
459 
460 	edChallEnd->setCaption(s);
461 	//ap->mWndChallEnd->setVisible(true);  // show after stage end
462 
463 	LogO("|]");
464 }
465 
466 
467 //  Prize const  * * *
468 const String
469 	CGui::clrPrize[4] = {"","#D0B050","#D8D8D8","#E8E050"},
470 	CGui::strPrize[4] = {"","#{Bronze}","#{Silver}","#{Gold}"};
471 
472 //  lowering pass req
473 const int   CGui::ciAddPos[3]    = {4,2,0};
474 const float CGui::cfSubPoints[3] = {2.f,1.f,0.f};
475 
StrPrize(int i)476 const String CGui::StrPrize(int i)  //0..3
477 {
478 	return clrPrize[i] + TR(strPrize[i]);
479 }
480 
481 
482 //  stage wnd text
483 //----------------------------------------------------------------------------------------------------------------------
ChallFillStageInfo(bool finished)484 void CGui::ChallFillStageInfo(bool finished)
485 {
486 	int chId = pSet->game.chall_num, p = pSet->game.champ_rev ? 1 : 0;
487 	const ProgressChall& pc = progressL[p].chs[chId];
488 	const ProgressTrackL& pt = pc.trks[pc.curTrack];
489 	const Chall& ch = data->chall->all[chId];
490 	const ChallTrack& trk = ch.trks[pc.curTrack];
491 	bool last = pc.curTrack+1 == ch.trks.size();
492 
493 	String s =
494 		"#80FFE0"+ TR("#{Challenge}") + ":  " + ch.name + "\n\n" +
495 		"#80FFC0"+ TR("#{Stage}") + ":  " + toStr(pc.curTrack+1) + " / " + toStr(ch.trks.size()) + "\n" +
496 		"#80FF80"+ TR("#{Track}") + ":  " + trk.name + "\n\n";
497 
498 	if (!finished)  // track info at start
499 	{
500 		int id = data->tracks->trkmap[trk.name];
501 		if (id > 0)
502 		{
503 			const TrackInfo* ti = &data->tracks->trks[id-1];
504 			s += "#A0D0FF"+ TR("#{Difficulty}:  ") + gcom->clrsDiff[ti->diff] + TR("#{Diff"+toStr(ti->diff)+"}") + "\n";
505 			if (app->scn->road)
506 			{	Real len = app->scn->road->st.Length*0.001f * (pSet->show_mph ? 0.621371f : 1.f);
507 				s += "#A0D0FF"+ TR("#{Distance}:  ") + "#B0E0FF" +
508 					fToStr(len, 1,4) + (pSet->show_mph ? TR(" #{UnitMi}") : TR(" #{UnitKm}")) + "\n\n";
509 		}	}
510 	}
511 
512 	if (finished)  // stage
513 	{
514 		s += "#80C0FF"+TR("#{Finished}.") + "\n\n";
515 
516 		///  Pass Stage  --------------
517 		bool passed = true, pa;
518 		if (trk.timeNeeded > 0.f)
519 		{
520 			pa = pt.time <= trk.timeNeeded;
521 			s += TR("#D8C0FF#{TBTime}: ") + StrTime(pt.time) + "  / " + StrTime(trk.timeNeeded) + sPass(pa) +"\n";
522 			passed &= pa;
523 		}
524 		if (trk.passPoints > 0.f)
525 		{
526 			pa = pt.points >= trk.passPoints;
527 			s += TR("#D8C0FF#{TBPoints}: ") + fToStr(pt.points,1) + "  / " + fToStr(trk.passPoints,1) + sPass(pa) +"\n";
528 			passed &= pa;
529 		}
530 		if (trk.passPos > 0)
531 		{
532 			pa = pt.pos <= trk.passPos;
533 			s += TR("#D8C0FF#{TBPosition}: ") + toStr(pt.pos) + "  / " + toStr(trk.passPos) + sPass(pa) +"\n";
534 			passed &= pa;
535 		}
536 
537 		if (passed)	s += "\n\n"+TR(last ? "#00FF00#{Continue}." : "#00FF00#{NextStage}.");
538 		else		s += "\n\n"+TR("#FF6000#{RepeatStage}.");
539 	}
540 	else
541 	{	///  Pass needed  --------------
542 		s += "#F0F060"+TR("#{Needed}") +"\n";
543 		if (trk.timeNeeded > 0.f)	s += TR("  #D8C0FF#{TBTime}: ") + StrTime(trk.timeNeeded) +"\n";
544 		if (trk.passPoints > 0.f)	s += TR("  #D8C0FF#{TBPoints}: ") + fToStr(trk.passPoints,1) +"\n";
545 		if (trk.passPos > 0)		s += TR("  #D8C0FF#{TBPosition}: ") + toStr(trk.passPos) +"\n";
546 		if (app->scn->road)			s += "\n#A8B8C8"+ app->scn->road->sTxtDesc;
547 	}
548 
549 	edChallStage->setCaption(s);
550 	btChallStage->setCaption(finished ? TR("#{Continue}") : TR("#{Start}"));
551 
552 	//  preview image at start
553 	if (!finished)
554 	{
555 		String path = gcom->PathListTrkPrv(0, trk.name);
556 		app->prvStCh.Load(path+"view.jpg");
557 	}
558 }
559 
560 
561 //  chall details  (gui tab Stages)
562 //----------------------------------------------------------------------------------------------------------------------
UpdChallDetail(int id)563 void CGui::UpdChallDetail(int id)
564 {
565 	const Chall& ch = data->chall->all[id];
566 	int ntrks = ch.trks.size();
567 	int p = pSet->gui.champ_rev ? 1 : 0;
568 
569 	String s1,s2,clr;
570 	//s1 += "\n";  s2 += "\n";
571 
572 	//  track  --------
573 	clr = gcom->clrsDiff[ch.diff];
574 	s1 += clr+ TR("#{Difficulty}\n");    s2 += clr+ TR("#{Diff"+toStr(ch.diff)+"}")+"\n";
575 
576 	clr = gcom->clrsDiff[std::min(8,ntrks*2/3+1)];
577 	s1 += clr+ TR("#{Tracks}\n");        s2 += clr+ toStr(ntrks)+"\n";
578 
579 	//s1 += "\n";  s2 += "\n";
580 	clr = gcom->clrsDiff[std::min(8,int(ch.time/3.f/60.f))];
581 	s1 += TR("#80F0E0#{Time} [#{TimeMS}.]\n"); s2 += "#C0FFE0"+clr+ StrTime2(ch.time)+"\n";
582 
583 	//  cars  --------
584 	s1 += "\n";  s2 += "\n";
585 	s1 += TR("#F08080#{Vehicles}\n");        s2 += "#FFA0A0" + StrChallCars(ch)+"\n";
586 	if (ch.carChng)
587 	{	s1 += TR("#C0B0B0#{CarChange}\n");  s2 += TR("#A0B8A0#{Allowed}")+"\n";  }
588 
589 
590 	//  game  --------
591 	s1 += "\n";  s2 += "\n";
592 	s1 += TR("#D090E0#{Game}")+"\n";     s2 += "\n";
593 	#define cmbs(cmb, i)  (i>=0 ? cmb->getItemNameAt(i) : TR("#{Any}"))
594 	s1 += TR("#A0B0C0  #{Simulation}\n");  s2 += "#B0C0D0"+ ch.sim_mode +"\n";
595 	s1 += TR("#A090E0  #{Damage}\n");      s2 += "#B090FF"+ cmbs(cmbDamage, ch.damage_type) +"\n";
596 	s1 += TR("#B080C0  #{InputMapRewind}\n"); s2 += "#C090D0"+ cmbs(cmbRewind, ch.rewind_type) +"\n";
597 	//s1 += "\n";  s2 += "\n";
598 	s1 += TR("#80C0FF  #{Boost}\n");       s2 += "#90D0FF"+ cmbs(cmbBoost, ch.boost_type) +"\n";
599 	s1 += TR("#6098A0  #{Flip}\n");        s2 += "#7098A0"+ cmbs(cmbFlip, ch.flip_type) +"\n";
600 
601 	//  hud  --------
602 	//s1 += "\n";  s2 += "\n";
603 	//bool minimap, chk_arr, chk_beam, trk_ghost;  // deny using it if false
604 
605 	txtCh->setCaption(s1);  valCh->setCaption(s2);
606 
607 
608 	//  pass challenge  --------
609 	s1 = "";  s2 = "";
610 	if (ch.totalTime > 0.f || ch.avgPoints > 0.f || ch.avgPos > 0.f)
611 	{
612 		s1 += "\n";  s2 += "\n";  int p, pp = ch.prizes;
613 		s1 += TR("#F0F060#{Needed} - #{Challenge}")+"\n";   s2 += "\n";
614 		s1 += "#D8C0FF";  s2 += "#F0D8FF";
615 		if (ch.avgPoints > 0.f){	s1 += TR("  #{TBPoints}\n");
616 									for (p=0; p <= pp; ++p)  s2 += clrPrize[2-pp+ p+1]+
617 											fToStr(ch.avgPoints - cfSubPoints[p] * ch.factor ,1,3)+"  ";
618 									s2 += "\n";  }
619 		if (ch.avgPos > 0.f)  {		s1 += TR("  #{TBPosition}\n");
620 									for (p=0; p <= pp; ++p)  s2 += clrPrize[2-pp+ p+1]+
621 											fToStr(ch.avgPos + ciAddPos[p] * ch.factor ,1,3)+"  ";
622 									s2 += "\n";  }
623 		if (ch.totalTime > 0.f){	s1 += TR("  #{TBTime}\n");
624 									s2 += StrTime(ch.totalTime)+"\n";  }
625 	}
626 	txtChP[1]->setCaption(s1);  valChP[1]->setCaption(s2);
627 
628 	//  pass stage  --------
629 	s1 = "";  s2 = "";
630 	size_t t = liStages->getIndexSelected();
631 	if (t != ITEM_NONE)
632 	{
633 		const ChallTrack& trk = ch.trks[t];
634 		if (trk.timeNeeded > 0.f || trk.passPoints > 0.f || trk.passPos > 0)
635 		{
636 			s1 += "\n";  s2 += "\n";
637 			s1 += TR("#FFC060#{Needed} - #{Stage}")+"\n";   s2 += "\n";
638 			s1 += "#D8C0FF";   s2 += "#F0D8FF";
639 			if (trk.passPoints > 0.f){	s1 += TR("  #{TBPoints}\n");    s2 += fToStr(trk.passPoints,2,5)+"\n";  }
640 			if (trk.passPos > 0.f)  {	s1 += TR("  #{TBPosition}\n");  s2 += fToStr(trk.passPos,2,5)+"\n";  }
641 			if (trk.timeNeeded > 0.f){	s1 += TR("  #{TBTime}\n");      s2 += StrTime(trk.timeNeeded)+"\n";  }
642 	}	}
643 	txtChP[0]->setCaption(s1);  valChP[0]->setCaption(s2);
644 
645 
646 	//  progress  --------
647 	s1 = "";  s2 = "";
648 	const ProgressChall& pc = progressL[p].chs[id];
649 	int cur = pc.curTrack, all = data->chall->all[id].trks.size();
650 	if (cur > 0)
651 	{
652 		s1 += TR("#B0FFFF#{Progress}\n");    s2 += "#D0FFFF"+(cur == all ? TR("#{Finished}").asUTF8() : fToStr(100.f * cur / all,0,3)+" %")+"\n";
653 		s1 += TR("#F8FCFF#{Prize}\n");       s2 += StrPrize(pc.fin+1)+"\n";
654 		#define clrP(b)  if (b)  {  s1 += "#C8D0F0";  s2 += "#E0F0FF";  }else{  s1 += "#80A0C0";  s2 += "#90B0D0";  }
655 		s1 += "\n";  s2 += "\n";  clrP(ch.avgPoints > 0.f);
656 		s1 += TR("  #{TBPoints}\n");    s2 += fToStr(pc.avgPoints,2,5)+"\n";  clrP(ch.avgPos > 0.f);
657 		s1 += TR("  #{TBPosition}\n");  s2 += fToStr(pc.avgPos,2,5)+"\n";  clrP(ch.totalTime > 0.f);
658 		s1 += TR("  #{TBTime}\n");      s2 += StrTime(pc.totalTime)+"\n";
659 	}
660 	txtChP[2]->setCaption(s1);  valChP[2]->setCaption(s2);
661 
662 
663 	//  btn start
664 	s1 = cur == all ? TR("#{Restart}") : (cur == 0 ? TR("#{Start}") : TR("#{Continue}"));
665 	btStChall->setCaption(s1);
666 	btChRestart->setVisible(cur > 0);
667 }
668