1 /***************************************************************************
2 rttrack.cpp -- Track utilities functions
3 -------------------
4 created : Sat Aug 14 23:03:22 CEST 1999
5 copyright : (C) 1999-2014 by Eric Espie, Bernhard Wymann
6 email : torcs@free.fr
7 version : $Id: rttrack.cpp,v 1.21.2.16 2014/05/20 14:07:08 berniw Exp $
8 ***************************************************************************/
9
10 /***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19 /** @file
20 Common functions for robots.
21 @author Bernhard Wymann, Eric Espie
22 @version $Id: rttrack.cpp,v 1.21.2.16 2014/05/20 14:07:08 berniw Exp $
23 */
24
25 /** @defgroup tracktools Robot Track Tools API
26 API to gather information about the track and the cars state relative to the track.
27 @ingroup robottools
28 */
29
30 /** @defgroup setuptools Robot Setup Tools API
31 API to load and store car setups in a unified way, and to support setup changes during pit stops.
32 @ingroup robottools
33 */
34
35
36 #include <portability.h>
37 #include <stdlib.h>
38 #include <math.h>
39 #ifdef WIN32
40 #include <windows.h>
41 #endif
42 #include <tgf.h>
43 #include <car.h>
44 #include <track.h>
45
46 #include <robottools.h>
47
48 /** Get the track width at the specified point.
49 @ingroup tracktools
50 @param seg Segment
51 @param toStart Distance from the beginning of the segment.
52 <br>The units are:
53 - meters for straights
54 - radians for turns
55 @return Width of the track at this point.
56 @note The Pit lane and the track have different width, and the side segments have variable width.
57 */
58 tdble
RtTrackGetWidth(tTrackSeg * seg,tdble toStart)59 RtTrackGetWidth(tTrackSeg *seg, tdble toStart)
60 {
61 return fabs(seg->startWidth + toStart * seg->Kyl);
62 }
63
64
65 /** Convert a Local position (segment, toRight, toStart)
66 @ingroup tracktools
67 into a Global one (X, Y)
68 The ToStart position refers to the current segment,
69 the function will not search for next segment if toStart
70 is greater than the segment length.
71 toStart represent an angle in radian for curves
72 and a length in meters for straights.
73 @param p Local position
74 @param X returned X position
75 @param Y returned Y position
76 @param flag Local position use:
77 - TR_TOMIDDLE the toMiddle field is used
78 - TR_TORIGHT the toRight field is used
79 - TR_TOLEFT the toLeft field is used
80 */
81 void
RtTrackLocal2Global(tTrkLocPos * p,tdble * X,tdble * Y,int flag)82 RtTrackLocal2Global(tTrkLocPos *p, tdble *X, tdble *Y, int flag)
83 {
84 tdble CosA, SinA, r, a;
85 tdble tr;
86
87 tTrackSeg *seg = p->seg;
88 switch (flag) {
89 case TR_TOMIDDLE:
90 switch(seg->type) {
91 case TR_STR:
92 CosA = cos(seg->angle[TR_ZS]);
93 SinA = sin(seg->angle[TR_ZS]);
94 /* Jussi Pajala: must be divided by two to get middle of the track ! */
95 tr = p->toMiddle + seg->startWidth / 2.0;
96 *X = seg->vertex[TR_SR].x + p->toStart * CosA - tr * SinA;
97 *Y = seg->vertex[TR_SR].y + p->toStart * SinA + tr * CosA;
98 break;
99
100 case TR_LFT:
101 a = seg->angle[TR_ZS] + p->toStart;
102 r = seg->radius - p->toMiddle;
103 *X = seg->center.x + r * sin(a);
104 *Y = seg->center.y - r * cos(a);
105 break;
106
107 case TR_RGT:
108 a = seg->angle[TR_ZS] - p->toStart;
109 r = seg->radius + p->toMiddle;
110 *X = seg->center.x - r * sin(a);
111 *Y = seg->center.y + r * cos(a);
112 break;
113
114 }
115 break;
116
117 case TR_TORIGHT:
118 switch(seg->type) {
119 case TR_STR:
120 CosA = cos(seg->angle[TR_ZS]);
121 SinA = sin(seg->angle[TR_ZS]);
122 switch (seg->type2) {
123 case TR_MAIN:
124 case TR_LSIDE:
125 case TR_LBORDER:
126 tr = p->toRight;
127 break;
128 case TR_RSIDE:
129 case TR_RBORDER:
130 tr = p->toRight - seg->Kyl * p->toStart;
131 break;
132 default:
133 tr = 0;
134 break;
135 }
136 *X = seg->vertex[TR_SR].x + p->toStart * CosA - tr * SinA;
137 *Y = seg->vertex[TR_SR].y + p->toStart * SinA + tr * CosA;
138 break;
139
140 case TR_LFT:
141 a = seg->angle[TR_ZS] + p->toStart;
142 switch (seg->type2) {
143 case TR_MAIN:
144 case TR_LSIDE:
145 case TR_LBORDER:
146 r = seg->radiusr - p->toRight ;
147 break;
148 case TR_RSIDE:
149 case TR_RBORDER:
150 r = seg->radiusl + seg->startWidth + seg->Kyl * p->toStart - p->toRight ;
151 break;
152 default:
153 r = 0;
154 break;
155 }
156 *X = seg->center.x + r * sin(a);
157 *Y = seg->center.y - r * cos(a);
158 break;
159
160 case TR_RGT:
161 a = seg->angle[TR_ZS] - p->toStart;
162 switch (seg->type2) {
163 case TR_MAIN:
164 case TR_LSIDE:
165 case TR_LBORDER:
166 r = seg->radiusr + p->toRight ;
167 break;
168 case TR_RSIDE:
169 case TR_RBORDER:
170 r = seg->radiusl - seg->startWidth - seg->Kyl * p->toStart + p->toRight ;
171 break;
172 default:
173 r = 0;
174 break;
175 }
176 *X = seg->center.x - r * sin(a);
177 *Y = seg->center.y + r * cos(a);
178 break;
179 }
180 break;
181
182 case TR_TOLEFT:
183 switch(seg->type) {
184 case TR_STR:
185 CosA = cos(seg->angle[TR_ZS]);
186 SinA = sin(seg->angle[TR_ZS]);
187 tr = seg->startWidth + seg->Kyl * p->toStart - p->toLeft;
188 *X = seg->vertex[TR_SR].x + p->toStart * CosA - tr * SinA;
189 *Y = seg->vertex[TR_SR].y + p->toStart * SinA + tr * CosA;
190 break;
191
192 case TR_LFT:
193 a = seg->angle[TR_ZS] + p->toStart;
194 r = seg->radiusl + p->toLeft;
195 *X = seg->center.x + r * sin(a);
196 *Y = seg->center.y - r * cos(a);
197 break;
198
199 case TR_RGT:
200 a = seg->angle[TR_ZS] - p->toStart;
201 r = seg->radiusr + seg->startWidth + seg->Kyl * p->toStart - p->toLeft;
202 *X = seg->center.x - r * sin(a);
203 *Y = seg->center.y + r * cos(a);
204 break;
205 }
206 break;
207 }
208 }
209
210 /** Convert a Global (segment, X, Y) position into a Local one (segment, toRight, toStart)
211 @ingroup tracktools
212 The segment in the Global position is used to start the search of a good segment
213 in term of toStart value.
214 The segments are scanned in order to find a toStart value between 0 and the length
215 of the segment for straights or the arc of the curve.
216 The sides parameters is to indicate wether to use the track sides (1) or not (0) in
217 the toRight computation.
218 @param segment Current segment
219 @param X Current X position
220 @param Y Current Y position
221 @param p Returned local position
222 @param type Type of local position desired:
223 - TR_LPOS_MAIN relative to the main segment
224 - TR_LPOS_SEGMENT if the point is on a side, relative to this side
225 - TR_LPOS_TRACK local pos includes all the track width
226 */
227
228 void
RtTrackGlobal2Local(tTrackSeg * segment,tdble X,tdble Y,tTrkLocPos * p,int type)229 RtTrackGlobal2Local(tTrackSeg *segment, tdble X, tdble Y, tTrkLocPos *p, int type)
230 {
231 int segnotfound = 1;
232 tdble x, y;
233 tTrackSeg *seg = segment;
234 tTrackSeg *sseg;
235 tdble theta, a2;
236 int depl = 0;
237 tdble curWidth;
238
239 p->type = type;
240
241 while (segnotfound) {
242
243 switch(seg->type) {
244 case TR_STR:
245 /* rotation */
246 tdble sine, cosine;
247 tdble ts;
248
249 sine = sin(seg->angle[TR_ZS]);
250 cosine = cos(seg->angle[TR_ZS]);
251 x = X - seg->vertex[TR_SR].x;
252 y = Y - seg->vertex[TR_SR].y;
253 ts = x * cosine + y * sine;
254 p->seg = seg;
255 p->toStart = ts;
256 p->toRight = y * cosine - x * sine;
257 if ((ts < 0) && (depl < 1)) {
258 /* get back */
259 seg = seg->prev;
260 depl = -1;
261 } else if ((ts > seg->length) && (depl > -1)) {
262 seg = seg->next;
263 depl = 1;
264 } else {
265 segnotfound = 0;
266 }
267 break;
268
269 case TR_LFT:
270 /* rectangular to polar */
271 x = X - seg->center.x;
272 y = Y - seg->center.y;
273 a2 = seg->arc / 2.0;
274 theta = atan2(y, x) - (seg->angle[TR_CS] + a2);
275 NORM_PI_PI(theta);
276 p->seg = seg;
277 p->toStart = theta + a2;
278 p->toRight = seg->radiusr - sqrt(x*x + y*y);
279 if ((theta < -a2) && (depl < 1)) {
280 seg = seg->prev;
281 depl = -1;
282 } else if ((theta > a2) && (depl > -1)) {
283 seg = seg->next;
284 depl = 1;
285 } else {
286 segnotfound = 0;
287 }
288 break;
289
290 case TR_RGT:
291 /* rectangular to polar */
292
293 x = X - seg->center.x;
294 y = Y - seg->center.y;
295 a2 = seg->arc / 2.0;
296 theta = seg->angle[TR_CS] - a2 - atan2(y, x);
297 NORM_PI_PI(theta);
298 p->seg = seg;
299 p->toStart = theta + a2;
300 p->toRight = sqrt(x*x + y*y) - seg->radiusr;
301 if ((theta < -a2) && (depl < 1)) {
302 seg = seg->prev;
303 depl = -1;
304 } else if ((theta > a2) && (depl > -1)) {
305 seg = seg->next;
306 depl = 1;
307 } else {
308 segnotfound = 0;
309 }
310 break;
311 }
312 }
313
314 /* The track is of constant width */
315 /* This is subject to change */
316 p->toMiddle = p->toRight - seg->width / 2.0;
317 p->toLeft = seg->width - p->toRight;
318
319 /* Consider all the track with the sides */
320 /* Stay on main segment */
321 if (type == TR_LPOS_TRACK) {
322 if (seg->rside != NULL) {
323 sseg = seg->rside;
324 p->toRight += RtTrackGetWidth(sseg, p->toStart);
325 sseg = sseg->rside;
326 if (sseg) {
327 p->toRight += RtTrackGetWidth(sseg, p->toStart);
328 }
329 }
330 if (seg->lside != NULL) {
331 sseg = seg->lside;
332 p->toLeft += RtTrackGetWidth(sseg, p->toStart);
333 sseg = sseg->lside;
334 if (sseg) {
335 p->toLeft += RtTrackGetWidth(sseg, p->toStart);
336 }
337 }
338 }
339
340 /* Relative to a segment, change to the side segment if necessary */
341 if (type == TR_LPOS_SEGMENT) {
342 if ((p->toRight < 0) && (seg->rside != NULL)) {
343 sseg = seg->rside;
344 p->seg = sseg;
345 curWidth = RtTrackGetWidth(sseg, p->toStart);
346 p->toRight += curWidth;
347 p->toLeft -= seg->width;
348 p->toMiddle += (seg->width + curWidth) / 2.0;
349 if ((p->toRight < 0) && (sseg->rside != NULL)) {
350 p->toLeft -= curWidth;
351 p->toMiddle += curWidth / 2.0;
352 seg = sseg;
353 sseg = seg->rside;
354 curWidth = RtTrackGetWidth(sseg, p->toStart);
355 p->seg = sseg;
356 p->toRight += curWidth;
357 p->toMiddle += curWidth / 2.0;
358 }
359 } else if ((p->toLeft < 0) && (seg->lside != NULL)) {
360 sseg = seg->lside;
361 p->seg = sseg;
362 curWidth = RtTrackGetWidth(sseg, p->toStart);
363 p->toRight += -seg->width;
364 p->toMiddle -= (seg->width + curWidth) / 2.0;
365 p->toLeft += curWidth;
366 if ((p->toLeft < 0) && (sseg->lside != NULL)) {
367 p->toRight -= curWidth;
368 p->toMiddle -= curWidth / 2.0;
369 seg = sseg;
370 sseg = seg->lside;
371 curWidth = RtTrackGetWidth(sseg, p->toStart);
372 p->seg = sseg;
373 p->toMiddle -= curWidth / 2.0;
374 p->toLeft += curWidth;
375 }
376 }
377 }
378 }
379
380 /** Returns the absolute height in meters of the road
381 at the Local position p.
382 If the point lies outside the track (and sides)
383 the height is computed using the tangent to the banking
384 of the segment (or side).
385 @verbatim
386 + Point given
387 .^
388 . |
389 . |
390 . |
391 / | heigth
392 / |
393 ______/ v
394 ^ ^^ ^
395 | || |
396 track side
397 @endverbatim
398 @ingroup tracktools
399 @param p Local position
400 @return Height in meters
401 */
402 tdble
RtTrackHeightL(tTrkLocPos * p)403 RtTrackHeightL(tTrkLocPos *p)
404 {
405 tdble lg;
406 tdble tr = p->toRight;
407 tTrackSeg *seg = p->seg;
408
409 //bool left_side = true;
410 if ((tr < 0) && (seg->rside != NULL)) {
411 //left_side = false;
412
413 seg = seg->rside;
414 tr += seg->width;
415
416 if ((tr < 0) && (seg->rside != NULL)) {
417 seg = seg->rside;
418 tr += RtTrackGetWidth(seg, p->toStart);
419 }
420 } else if ((tr > seg->width) && (seg->lside != NULL)) {
421 tr -= seg->width;
422 seg = seg->lside;
423 if ((tr > seg->width) && (seg->lside != NULL)) {
424 tr -= RtTrackGetWidth(seg, p->toStart);
425 seg = seg->lside;
426 }
427 }
428
429 switch (seg->type) {
430 case TR_STR:
431 lg = p->toStart;
432 break;
433 default:
434 lg = p->toStart * seg->radius;
435 break;
436 }
437
438 if (seg->style == TR_CURB) {
439 // The final height = starting height + height difference due
440 // to track angle + height difference due to curb (this seems
441 // to be the way it is implemented in the graphics too: the
442 // curb does not adding an angle to the main track, but a
443 // height in global coords).
444 if (seg->type2 == TR_RBORDER) {
445 // alpha shows how far we've moved into this segment.
446 tdble alpha = seg->width - tr;
447 tdble angle = seg->angle[TR_XS] + p->toStart * seg->Kzw;
448 tdble noise = seg->surface->kRoughness * sin(seg->surface->kRoughWaveLen * lg) * alpha / seg->width;
449 tdble start_height = seg->vertex[TR_SR].z + p->toStart * seg->Kzl;
450 return start_height + tr * tan(angle) + alpha * atan2(seg->height, seg->width) + noise;
451 }
452
453 return
454 seg->vertex[TR_SR].z + p->toStart * seg->Kzl +
455 tr * (tan(seg->angle[TR_XS] + p->toStart * seg->Kzw) +
456 atan2(seg->height, seg->width)) +
457 seg->surface->kRoughness * sin(seg->surface->kRoughWaveLen * lg) * tr / seg->width;
458 }
459
460 return seg->vertex[TR_SR].z + p->toStart * seg->Kzl + tr * tan(seg->angle[TR_XS] + p->toStart * seg->Kzw) +
461 seg->surface->kRoughness * sin(seg->surface->kRoughWaveLen * tr) * sin(seg->surface->kRoughWaveLen * lg);
462 }
463
464 /* get the real segment */
465 tTrackSeg *
RtTrackGetSeg(tTrkLocPos * p)466 RtTrackGetSeg(tTrkLocPos *p)
467 {
468 tdble tr = p->toRight;
469 tTrackSeg *seg = p->seg;
470
471 if ((tr < 0) && (seg->rside != NULL)) {
472 seg = seg->rside;
473 tr += seg->width;
474 if ((tr < 0) && (seg->rside != NULL)) {
475 seg = seg->rside;
476 tr += RtTrackGetWidth(seg, p->toStart);
477 }
478 } else if ((tr > seg->width) && (seg->lside != NULL)) {
479 tr -= seg->width;
480 seg = seg->lside;
481 if ((tr > seg->width) && (seg->lside != NULL)) {
482 tr -= RtTrackGetWidth(seg, p->toStart);
483 seg = seg->lside;
484 }
485 }
486 return seg;
487 }
488
489 /** Returns the absolute height in meters of the road
490 at the Global position (segment, X, Y)
491 @ingroup tracktools
492 @param seg Segment
493 @param X Global X position
494 @param Y Global Y position
495 @return Height in meters
496 */
497 tdble
RtTrackHeightG(tTrackSeg * seg,tdble X,tdble Y)498 RtTrackHeightG(tTrackSeg *seg, tdble X, tdble Y)
499 {
500 tTrkLocPos p;
501
502 RtTrackGlobal2Local(seg, X, Y, &p, TR_LPOS_SEGMENT);
503 return RtTrackHeightL(&p);
504 }
505
506 /** Give the normal vector of the border of the track
507 including the sides.
508 The side parameter is used to indicate the right (TR_RGT)
509 of the left (TR_LFT) side to consider.
510 The Global position given (segment, X, Y) is used
511 to project the point on the border, it is not necessary
512 to give a point directly on the border itself.
513 The vector is normalized.
514 @ingroup tracktools
515 @param seg Current segment
516 @param X Global X position
517 @param Y Global Y position
518 @param side Side where the normal is wanted
519 - TR_LFT for left side
520 - TR_RGT for right side
521 @param norm Returned normalized side normal vector
522 @todo RtTrackSideNormalG: Give the correct normal for variable width sides.
523 */
524 void
RtTrackSideNormalG(tTrackSeg * seg,tdble X,tdble Y,int side,t3Dd * norm)525 RtTrackSideNormalG(tTrackSeg *seg, tdble X, tdble Y, int side, t3Dd *norm)
526 {
527 tdble lg;
528
529 switch (seg->type) {
530 case TR_STR:
531 if (side == TR_RGT) {
532 norm->x = seg->rgtSideNormal.x;
533 norm->y = seg->rgtSideNormal.y;
534 } else {
535 norm->x = -seg->rgtSideNormal.x;
536 norm->y = -seg->rgtSideNormal.y;
537 }
538 break;
539 case TR_RGT:
540 if (side == TR_LFT) {
541 norm->x = seg->center.x - X;
542 norm->y = seg->center.y - Y;
543 } else {
544 norm->x = X - seg->center.x;
545 norm->y = Y - seg->center.y;
546 }
547 lg = 1.0 / sqrt(norm->x * norm->x + norm->y * norm->y);
548 norm->x *= lg;
549 norm->y *= lg;
550 break;
551 case TR_LFT:
552 if (side == TR_RGT) {
553 norm->x = seg->center.x - X;
554 norm->y = seg->center.y - Y;
555 } else {
556 norm->x = X - seg->center.x;
557 norm->y = Y - seg->center.y;
558 }
559 lg = 1.0 / sqrt(norm->x * norm->x + norm->y * norm->y);
560 norm->x *= lg;
561 norm->y *= lg;
562 break;
563 }
564 }
565
566 /** Used to get the tangent angle for a track position
567 The angle is given in radian.
568 the angle 0 is parallel to the first segment start.
569 @ingroup tracktools
570 @param p Local position
571 @return Tagent angle in radian.
572 @note For side segment, the track side is used for the tangent.
573 */
574
575 tdble
RtTrackSideTgAngleL(tTrkLocPos * p)576 RtTrackSideTgAngleL(tTrkLocPos *p)
577 {
578 switch (p->seg->type) {
579 case TR_STR:
580 return p->seg->angle[TR_ZS];
581 break;
582 case TR_RGT:
583 return p->seg->angle[TR_ZS] - p->toStart;
584 break;
585 case TR_LFT:
586 return p->seg->angle[TR_ZS] + p->toStart;
587 break;
588 }
589 return 0;
590 }
591
592
593 /** Used to get the normal vector of the road (pointing upward).
594 Local coordinates are used to locate the point where to
595 get the road normal vector.
596 The vector is normalized.
597 @ingroup tracktools
598 @param p Local position
599 @param norm Returned normalized road normal vector
600 */
601 void
RtTrackSurfaceNormalL(tTrkLocPos * p,t3Dd * norm)602 RtTrackSurfaceNormalL(tTrkLocPos *p, t3Dd *norm)
603 {
604 tTrkLocPos p1;
605 t3Dd px1, px2, py1, py2;
606 t3Dd v1, v2;
607 tdble lg;
608
609 p1.seg = p->seg;
610
611 p1.toStart = 0;
612 p1.toRight = p->toRight;
613 RtTrackLocal2Global(&p1, &px1.x, &px1.y, TR_TORIGHT);
614 px1.z = RtTrackHeightL(&p1);
615
616 if (p1.seg->type == TR_STR) {
617 p1.toStart = p1.seg->length;
618 } else {
619 p1.toStart = p1.seg->arc;
620 }
621
622 RtTrackLocal2Global(&p1, &px2.x, &px2.y, TR_TORIGHT);
623 px2.z = RtTrackHeightL(&p1);
624
625 p1.toRight = 0;
626 p1.toStart = p->toStart;
627 RtTrackLocal2Global(&p1, &py1.x, &py1.y, TR_TORIGHT);
628 py1.z = RtTrackHeightL(&p1);
629
630 p1.toRight = p1.seg->width;
631 RtTrackLocal2Global(&p1, &py2.x, &py2.y, TR_TORIGHT);
632 py2.z = RtTrackHeightL(&p1);
633
634
635 v1.x = px2.x - px1.x;
636 v1.y = px2.y - px1.y;
637 v1.z = px2.z - px1.z;
638 v2.x = py2.x - py1.x;
639 v2.y = py2.y - py1.y;
640 v2.z = py2.z - py1.z;
641
642 norm->x = v1.y * v2.z - v2.y * v1.z;
643 norm->y = v2.x * v1.z - v1.x * v2.z;
644 norm->z = v1.x * v2.y - v2.x * v1.y;
645 lg = sqrt(norm->x * norm->x + norm->y * norm->y + norm->z * norm->z);
646
647 if (lg == 0.0) {
648 lg = 1.0;
649 } else {
650 lg = 1.0 / lg;
651 }
652
653 norm->x *= lg;
654 norm->y *= lg;
655 norm->z *= lg;
656 }
657
658
659 /** Get the distance from the start lane.
660 @ingroup tracktools
661 @param car the concerned car.
662 @return The distance between the start lane and the car.
663 */
664 tdble
RtGetDistFromStart(tCarElt * car)665 RtGetDistFromStart(tCarElt *car)
666 {
667 tTrackSeg *seg;
668 tdble lg;
669
670 seg = car->_trkPos.seg;
671 lg = seg->lgfromstart;
672
673 switch (seg->type) {
674 case TR_STR:
675 lg += car->_trkPos.toStart;
676 break;
677 default:
678 lg += car->_trkPos.toStart * seg->radius;
679 break;
680 }
681
682 return lg;
683 }
684
685 /** Get the distance from the start lane.
686 @ingroup tracktools
687 @param p Local position
688 @return The distance between the start lane and the car.
689 */
690 tdble
RtGetDistFromStart2(tTrkLocPos * p)691 RtGetDistFromStart2(tTrkLocPos *p)
692 {
693 tTrackSeg *seg;
694 tdble lg;
695
696 seg = p->seg;
697 lg = seg->lgfromstart;
698
699 switch (seg->type) {
700 case TR_STR:
701 lg += p->toStart;
702 break;
703 default:
704 lg += p->toStart * seg->radius;
705 break;
706 }
707
708 return lg;
709 }
710
711
712 /** Get the distance to the pit stop.
713 @ingroup tracktools
714 @param car The concerned car.
715 @param track The current Track
716 @param dL Length to the pits
717 @param dW Width to the pits
718 @return 0
719 @note dW > 0 if the pit is on the right
720 */
721 int
RtDistToPit(struct CarElt * car,tTrack * track,tdble * dL,tdble * dW)722 RtDistToPit(struct CarElt *car, tTrack *track, tdble *dL, tdble *dW)
723 {
724 tTrkLocPos *pitpos;
725 tTrkLocPos *carpos;
726 tdble pitts;
727 tdble carts;
728
729 if (car->_pit == NULL) return 1;
730
731 pitpos = &(car->_pit->pos);
732 carpos = &(car->_trkPos);
733
734 if (carpos->seg->radius) {
735 carts = carpos->toStart * carpos->seg->radius;
736 } else {
737 carts = carpos->toStart;
738 }
739
740 if (pitpos->seg->radius) {
741 pitts = pitpos->toStart * pitpos->seg->radius;
742 } else {
743 pitts = pitpos->toStart;
744 }
745
746 *dL = pitpos->seg->lgfromstart - carpos->seg->lgfromstart + pitts - carts;
747 if (*dL < 0.0f) {
748 *dL += track->length;
749 } else if (*dL > track->length) {
750 *dL -= track->length;
751 }
752
753 *dW = pitpos->toRight - carpos->toRight;
754
755 return 0;
756 }
757
758
RtReadCarPitSetupEntry(tCarPitSetupValue * v,const char * path,const char * key,void * hdle,bool minmaxonly)759 static void RtReadCarPitSetupEntry(tCarPitSetupValue* v, const char* path, const char* key, void *hdle, bool minmaxonly)
760 {
761 if (!minmaxonly) {
762 v->value = GfParmGetNum(hdle, path, key, (char*)NULL, 0.0f);
763 }
764 GfParmGetNumBoundaries(hdle, path, key, &v->min, &v->max);
765 }
766
767
768 /** Initialize tCarPitSetup from data in parameter set given in handle @e hdle.
769 * @ingroup setuptools
770 * @param[in] hdle Handle to setup parameter set
771 * @param[in,out] s Pointer to tCarPitSetup struct to initialize
772 * @param[in] minmaxonly If true, just the min/max values of s are modified
773 */
RtInitCarPitSetup(void * hdle,tCarPitSetup * s,bool minmaxonly)774 void RtInitCarPitSetup(void *hdle, tCarPitSetup* s, bool minmaxonly)
775 {
776 static const char *WheelSect[4] = {SECT_FRNTRGTWHEEL, SECT_FRNTLFTWHEEL, SECT_REARRGTWHEEL, SECT_REARLFTWHEEL};
777 static const char *SuspSect[4] = {SECT_FRNTRGTSUSP, SECT_FRNTLFTSUSP, SECT_REARRGTSUSP, SECT_REARLFTSUSP};
778 const int BUFSIZE = 256;
779 char path[BUFSIZE];
780
781 // Steer
782 RtReadCarPitSetupEntry(&s->steerLock, SECT_STEER, PRM_STEERLOCK, hdle, minmaxonly);
783
784 int i;
785 for (i=0; i < 4; i++) {
786 // Wheel
787 RtReadCarPitSetupEntry(&s->wheelcamber[i], WheelSect[i], PRM_CAMBER, hdle, minmaxonly);
788 RtReadCarPitSetupEntry(&s->wheeltoe[i], WheelSect[i], PRM_TOE, hdle, minmaxonly);
789 RtReadCarPitSetupEntry(&s->wheelrideheight[i], WheelSect[i], PRM_RIDEHEIGHT, hdle, minmaxonly);
790
791 // Suspension
792 RtReadCarPitSetupEntry(&s->suspspring[i], SuspSect[i], PRM_SPR, hdle, minmaxonly);
793 RtReadCarPitSetupEntry(&s->susppackers[i], SuspSect[i], PRM_PACKERS, hdle, minmaxonly);
794 RtReadCarPitSetupEntry(&s->suspslowbump[i], SuspSect[i], PRM_SLOWBUMP, hdle, minmaxonly);
795 RtReadCarPitSetupEntry(&s->suspslowrebound[i], SuspSect[i], PRM_SLOWREBOUND, hdle, minmaxonly);
796 RtReadCarPitSetupEntry(&s->suspfastbump[i], SuspSect[i], PRM_FASTBUMP, hdle, minmaxonly);
797 RtReadCarPitSetupEntry(&s->suspfastrebound[i], SuspSect[i], PRM_FASTREBOUND, hdle, minmaxonly);
798 }
799
800 // Brake
801 RtReadCarPitSetupEntry(&s->brakeRepartition, SECT_BRKSYST, PRM_BRKREP, hdle, minmaxonly);
802 RtReadCarPitSetupEntry(&s->brakePressure, SECT_BRKSYST, PRM_BRKPRESS, hdle, minmaxonly);
803
804 // Anti roll bar
805 static const char *ArbSect[2] = {SECT_FRNTARB, SECT_REARARB};
806 for (i=0; i < 2; i++) {
807 RtReadCarPitSetupEntry(&s->arbspring[i], ArbSect[i], PRM_SPR, hdle, minmaxonly);
808 }
809
810 // Third element
811 static const char *AxleSect[2] = {SECT_FRNTAXLE, SECT_REARAXLE};
812 for (i=0; i < 2; i++) {
813 RtReadCarPitSetupEntry(&s->thirdspring[i], AxleSect[i], PRM_SPR, hdle, minmaxonly);
814 RtReadCarPitSetupEntry(&s->thirdbump[i], AxleSect[i], PRM_SLOWBUMP, hdle, minmaxonly);
815 RtReadCarPitSetupEntry(&s->thirdrebound[i], AxleSect[i], PRM_SLOWREBOUND, hdle, minmaxonly);
816 RtReadCarPitSetupEntry(&s->thirdX0[i], AxleSect[i], PRM_SUSPCOURSE, hdle, minmaxonly);
817 }
818
819 // Gears
820 for (i=0; i < 8; i++) {
821 snprintf(path, BUFSIZE, "%s/%s/%d", SECT_GEARBOX, ARR_GEARS, i+1);
822 RtReadCarPitSetupEntry(&s->gearsratio[i], path, PRM_RATIO, hdle, minmaxonly);
823 }
824
825 // Wings
826 static const char *WingSect[2] = {SECT_FRNTWING, SECT_REARWING};
827 for (i=0; i < 2; i++) {
828 RtReadCarPitSetupEntry(&s->wingangle[i], WingSect[i], PRM_WINGANGLE, hdle, minmaxonly);
829 }
830
831 // Differentials
832 static const char *DiffSect[3] = {SECT_FRNTDIFFERENTIAL, SECT_REARDIFFERENTIAL, SECT_CENTRALDIFFERENTIAL};
833 for (i=0; i < 3; i++) {
834 RtReadCarPitSetupEntry(&s->diffratio[i], DiffSect[i], PRM_RATIO, hdle, minmaxonly);
835 RtReadCarPitSetupEntry(&s->diffmintqbias[i], DiffSect[i], PRM_MIN_TQ_BIAS, hdle, minmaxonly);
836 RtReadCarPitSetupEntry(&s->diffmaxtqbias[i], DiffSect[i], PRM_MAX_TQ_BIAS, hdle, minmaxonly);
837 RtReadCarPitSetupEntry(&s->diffslipbias[i], DiffSect[i], PRM_MAX_SLIP_BIAS, hdle, minmaxonly);
838 RtReadCarPitSetupEntry(&s->difflockinginputtq[i], DiffSect[i], PRM_LOCKING_TQ, hdle, minmaxonly);
839 RtReadCarPitSetupEntry(&s->difflockinginputbraketq[i], DiffSect[i], PRM_LOCKINGBRAKE_TQ, hdle, minmaxonly);
840
841 const char* type = GfParmGetStr(hdle, DiffSect[i], PRM_TYPE, VAL_DIFF_NONE);
842 if (strcmp(type, VAL_DIFF_LIMITED_SLIP) == 0) {
843 s->diffType[i] = tCarPitSetup::LIMITED_SLIP;
844 } else if (strcmp(type, VAL_DIFF_VISCOUS_COUPLER) == 0) {
845 s->diffType[i] = tCarPitSetup::VISCOUS_COUPLER;
846 } else if (strcmp(type, VAL_DIFF_SPOOL) == 0) {
847 s->diffType[i] = tCarPitSetup::SPOOL;
848 } else if (strcmp(type, VAL_DIFF_FREE) == 0) {
849 s->diffType[i] = tCarPitSetup::FREE;
850 } else {
851 s->diffType[i] = tCarPitSetup::NONE;
852 }
853 }
854 }
855
856
857 /** Array with names for rtCarPitSetupType enumeration */
858 static const char* CarPitSetupFilenames[6] = { "practice", "qualifying", "race", "backup1", "backup2", "backup3" };
859
860
861 /** Compose filename from given strings
862 * @ingroup setuptools
863 @param[in] type Setup type
864 @param[in] robidx Index of robot
865 @param[in] carname TORCS internal name car name
866 @param[in] trackname TORCS internal name name of track
867 @param[in,out] filename Buffer for result
868 @param[in] len Buffer size
869 */
RtGetCarPitSetupFilename(rtCarPitSetupType type,int robidx,const char * carname,const char * trackname,char * filename,const int len)870 void RtGetCarPitSetupFilename(
871 rtCarPitSetupType type,
872 int robidx,
873 const char* carname,
874 const char* trackname,
875 char* filename,
876 const int len
877 )
878 {
879 snprintf(filename, len, "%s_%s_%d_%s" , carname, trackname, robidx, CarPitSetupFilenames[type]);
880 }
881
882
883 /** Robottool internal: Set parameter if min != max, save as well min and max values
884 @ingroup setuptools
885 @param[in,out] hdlesetup Handle to parameter set to write into
886 @param[in] path path of parameter
887 @param[in] key key name
888 @param[in] unit unit to convert the result to (NULL if SI wanted)
889 @param[in] v tCarPitSetupValue to set
890 */
RtParmSetNum(void * hdlesetup,const char * path,const char * key,const char * unit,tCarPitSetupValue * v)891 static void RtParmSetNum(void* hdlesetup, const char* path, const char* key, const char* unit, tCarPitSetupValue* v)
892 {
893 // If min == max there is nothing to adjust, so we do not need to write the value.
894 if (fabs(v->min - v->max) >= 0.0001f) {
895 GfParmSetNumEx(hdlesetup, path, key, unit, GfParmSI2Unit(unit, v->value), GfParmSI2Unit(unit, v->min), GfParmSI2Unit(unit, v->max));
896 }
897 }
898
899
900 /** Save a custom car setup to a given filename. The setup is validated against the setup given in hdlecar.
901 @ingroup setuptools
902 @param[in] hdlecar Handle to "master setup" (parameter set) to validate against (min/max and other checks)
903 @param[in] s Pointer to tCarPitSetup struct to save
904 @param[in] filepath Full path to setup file
905 @param[in] carname TORCS internal name car name
906 */
RtSaveCarPitSetupFile(void * hdlecar,tCarPitSetup * s,const char * filepath,const char * carname)907 void RtSaveCarPitSetupFile(
908 void *hdlecar, // handle to car definition file, for min/max merge
909 tCarPitSetup* s, // the setup data to save
910 const char* filepath, // full path including filename and extension
911 const char* carname
912 )
913 {
914 const int BUFSIZE = 256;
915 char path[BUFSIZE];
916 void* hdlesetup = GfParmReadFile(filepath, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
917
918 // Steer
919 RtParmSetNum(hdlesetup, SECT_STEER, PRM_STEERLOCK, "deg", &s->steerLock);
920
921 static const char *WheelSect[4] = {SECT_FRNTRGTWHEEL, SECT_FRNTLFTWHEEL, SECT_REARRGTWHEEL, SECT_REARLFTWHEEL};
922 static const char *SuspSect[4] = {SECT_FRNTRGTSUSP, SECT_FRNTLFTSUSP, SECT_REARRGTSUSP, SECT_REARLFTSUSP};
923
924 int i;
925 for (i=0; i < 4; i++) {
926 // Wheel
927 RtParmSetNum(hdlesetup, WheelSect[i], PRM_CAMBER, "deg", &s->wheelcamber[i]);
928 RtParmSetNum(hdlesetup, WheelSect[i], PRM_TOE, "deg", &s->wheeltoe[i]);
929 RtParmSetNum(hdlesetup, WheelSect[i], PRM_RIDEHEIGHT, "mm", &s->wheelrideheight[i]);
930
931 // Suspension
932 RtParmSetNum(hdlesetup, SuspSect[i], PRM_SPR, "lbs/in", &s->suspspring[i]);
933 RtParmSetNum(hdlesetup, SuspSect[i], PRM_PACKERS, "mm", &s->susppackers[i]);
934 RtParmSetNum(hdlesetup, SuspSect[i], PRM_SLOWBUMP, "lbs/in/s", &s->suspslowbump[i]);
935 RtParmSetNum(hdlesetup, SuspSect[i], PRM_SLOWREBOUND, "lbs/in/s", &s->suspslowrebound[i]);
936 RtParmSetNum(hdlesetup, SuspSect[i], PRM_FASTBUMP, "lbs/in/s", &s->suspfastbump[i]);
937 RtParmSetNum(hdlesetup, SuspSect[i], PRM_FASTREBOUND, "lbs/in/s", &s->suspfastrebound[i]);
938 }
939
940 // Brake
941 RtParmSetNum(hdlesetup, SECT_BRKSYST, PRM_BRKREP, NULL, &s->brakeRepartition);
942 RtParmSetNum(hdlesetup, SECT_BRKSYST, PRM_BRKPRESS, "kPa", &s->brakePressure);
943
944 // Anti roll bar
945 static const char *ArbSect[2] = {SECT_FRNTARB, SECT_REARARB};
946 for (i=0; i < 2; i++) {
947 RtParmSetNum(hdlesetup, ArbSect[i], PRM_SPR, "lbs/in", &s->arbspring[i]);
948 }
949
950 // Third element
951 static const char *AxleSect[2] = {SECT_FRNTAXLE, SECT_REARAXLE};
952 for (i=0; i < 2; i++) {
953 RtParmSetNum(hdlesetup, AxleSect[i], PRM_SPR, "lbs/in", &s->thirdspring[i]);
954 RtParmSetNum(hdlesetup, AxleSect[i], PRM_SLOWBUMP, "lbs/in/s", &s->thirdbump[i]);
955 RtParmSetNum(hdlesetup, AxleSect[i], PRM_SLOWREBOUND, "lbs/in/s", &s->thirdrebound[i]);
956 RtParmSetNum(hdlesetup, AxleSect[i], PRM_SUSPCOURSE, "mm", &s->thirdX0[i]);
957 }
958
959 // Gears
960 for (i=0; i < 8; i++) {
961 snprintf(path, BUFSIZE, "%s/%s/%d", SECT_GEARBOX, ARR_GEARS, i+1);
962 RtParmSetNum(hdlesetup, path, PRM_RATIO, NULL, &s->gearsratio[i]);
963 }
964
965 // Wings
966 static const char *WingSect[2] = {SECT_FRNTWING, SECT_REARWING};
967 for (i=0; i < 2; i++) {
968 RtParmSetNum(hdlesetup, WingSect[i], PRM_WINGANGLE, "deg", &s->wingangle[i]);
969 }
970
971 // Differentials
972 static const char *DiffSect[3] = {SECT_FRNTDIFFERENTIAL, SECT_REARDIFFERENTIAL, SECT_CENTRALDIFFERENTIAL};
973 static const char *DiffType[5] = {VAL_DIFF_NONE, VAL_DIFF_SPOOL, VAL_DIFF_FREE, VAL_DIFF_LIMITED_SLIP, VAL_DIFF_VISCOUS_COUPLER};
974 for (i=0; i < 3; i++) {
975 RtParmSetNum(hdlesetup, DiffSect[i], PRM_RATIO, NULL, &s->diffratio[i]);
976 RtParmSetNum(hdlesetup, DiffSect[i], PRM_MIN_TQ_BIAS, NULL, &s->diffmintqbias[i]);
977 RtParmSetNum(hdlesetup, DiffSect[i], PRM_MAX_TQ_BIAS, NULL, &s->diffmaxtqbias[i]);
978 RtParmSetNum(hdlesetup, DiffSect[i], PRM_MAX_SLIP_BIAS, NULL, &s->diffslipbias[i]);
979 RtParmSetNum(hdlesetup, DiffSect[i], PRM_LOCKING_TQ, "N.m", &s->difflockinginputtq[i]);
980 RtParmSetNum(hdlesetup, DiffSect[i], PRM_LOCKINGBRAKE_TQ, "N.m", &s->difflockinginputbraketq[i]);
981
982 if (s->diffType[i] != tCarPitSetup::NONE) {
983 GfParmSetStr(hdlesetup, DiffSect[i], PRM_TYPE, DiffType[s->diffType[i]]);
984 }
985 }
986
987 hdlesetup = GfParmMergeHandles(hdlecar, hdlesetup, GFPARM_MMODE_DST | GFPARM_MMODE_RELDST);
988 GfParmWriteFile(filepath, hdlesetup, carname);
989 GfParmReleaseHandle(hdlesetup);
990 }
991
992
993 /** Save a custom car setup for a given robot, car, track and session (race, practice, qualifying, ...) type.
994 The setup is validated against the setup given in hdlecar.
995 @ingroup setuptools
996 @param[in] hdlecar Handle to "master setup" (parameter set) to validate against (min/max and other checks)
997 @param[in] s Pointer to tCarPitSetup struct to save
998 @param[in] type Setup type
999 @param[in] modulename Name of robot module without extension
1000 @param[in] robidx Index of robot
1001 @param[in] trackname TORCS internal name name of track
1002 @param[in] carname TORCS internal name car name
1003 @note Robot, car, track and session information are used to compose a standard setup filename
1004 @see RtSaveCarPitSetupFile
1005 */
RtSaveCarPitSetup(void * hdlecar,tCarPitSetup * s,rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname)1006 void RtSaveCarPitSetup(
1007 void *hdlecar,
1008 tCarPitSetup* s,
1009 rtCarPitSetupType type,
1010 const char* modulename,
1011 int robidx,
1012 const char* trackname,
1013 const char* carname
1014 )
1015 {
1016 const int filelen = 256;
1017 char filename[filelen];
1018 RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1019
1020 const int pathlen = 1024;
1021 char path[pathlen];
1022
1023 snprintf(path, pathlen, "%sdrivers/%s/setups", GetLocalDir(), modulename);
1024 if (GfCreateDir(path) == GF_DIR_CREATED) {
1025 snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1026 RtSaveCarPitSetupFile(hdlecar, s, path, carname);
1027 } else {
1028 GfError("RtSaveCarPitSetup, could not create %s\n", path);
1029 }
1030 }
1031
1032
1033 /** Checks if a specific car setup is available
1034 @ingroup setuptools
1035 @param[in] type Setup type
1036 @param[in] modulename Name of robot module without extension
1037 @param[in] robidx Index of robot
1038 @param[in] trackname TORCS internal name name of track
1039 @param[in] carname TORCS internal name car name
1040 @return True on success, false on failure
1041 */
RtCarPitSetupExists(rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname)1042 bool RtCarPitSetupExists(
1043 rtCarPitSetupType type,
1044 const char* modulename,
1045 int robidx,
1046 const char* trackname,
1047 const char* carname
1048 )
1049 {
1050 const int filelen = 256;
1051 char filename[filelen];
1052 RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1053
1054 const int pathlen = 1024;
1055 char path[pathlen];
1056
1057 snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1058 FILE* file = fopen(path, "r"); // TODO: maybe "stat" would be enough here
1059 if (file) {
1060 fclose(file);
1061 return true;
1062 }
1063
1064 return false;
1065 }
1066
1067
1068 /** Load a custom car setup from a given filename. The setup is validated against the setup given in hdlecar.
1069 @ingroup setuptools
1070 @param[in] hdlecar Handle to "master setup" (parameter set) to validate against (min/max and other checks)
1071 @param[in] filepath Full path to setup file
1072 @param[in,out] s Pointer to tCarPitSetup struct to fill/initialize
1073 @param[in] minmaxonly If true, just the min/max values of s are modified
1074 @return True on success, false on failure
1075 */
RtLoadCarPitSetupFilename(void * hdlecar,const char * filepath,tCarPitSetup * s,bool minmaxonly)1076 bool RtLoadCarPitSetupFilename(void* hdlecar, const char* filepath, tCarPitSetup* s, bool minmaxonly)
1077 {
1078 void* hdlesetup = GfParmReadFile(filepath, GFPARM_RMODE_STD);
1079 if (hdlesetup) {
1080 hdlesetup = GfParmMergeHandles(hdlecar, hdlesetup, GFPARM_MMODE_DST | GFPARM_MMODE_RELDST);
1081 RtInitCarPitSetup(hdlesetup, s, minmaxonly);
1082 GfParmReleaseHandle(hdlesetup);
1083 return true;
1084 } else {
1085 return false;
1086 }
1087 }
1088
1089
1090 /** Load a custom car setup for a given robot, car, track and session (race, practice, qualifying, ...) type.
1091 The setup is validated against the setup given in hdlecar.
1092 @ingroup setuptools
1093 @param[in] hdlecar Handle to "master setup" (parameter set) to validate against (min/max and other checks)
1094 @param[in,out] s Pointer to tCarPitSetup struct to fill/initialize
1095 @param[in] type Setup type
1096 @param[in] modulename Name of robot module without extension
1097 @param[in] robidx Index of robot
1098 @param[in] trackname TORCS internal name name of track
1099 @param[in] carname TORCS internal name car name
1100 @param[in] minmaxonly If true, just the min/max values of s are modified
1101 @return true on success, false on failure
1102 @note Robot, car, track and session information are used to compose a standard setup filename
1103 @see RtLoadCarPitSetupFilename
1104 */
RtLoadCarPitSetup(void * hdlecar,tCarPitSetup * s,rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname,bool minmaxonly)1105 bool RtLoadCarPitSetup(
1106 void* hdlecar,
1107 tCarPitSetup* s,
1108 rtCarPitSetupType type,
1109 const char* modulename,
1110 int robidx,
1111 const char* trackname,
1112 const char* carname,
1113 bool minmaxonly
1114 )
1115 {
1116 const int filelen = 256;
1117 char filename[filelen];
1118 RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1119
1120 const int pathlen = 1024;
1121 char path[pathlen];
1122
1123 snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1124 return RtLoadCarPitSetupFilename(hdlecar, path, s, minmaxonly);
1125 }
1126
1127
1128 /** Gets a handle to a parameter file containing the original TORCS car setup, that means the car setup
1129 merged with the cars category setup
1130 @ingroup setuptools
1131 @param[in] carname TORCS internal name of the car (directory/filename)
1132 @return NULL on failure, a valid handle otherwise
1133 */
RtLoadOriginalCarSettings(const char * carname)1134 void* RtLoadOriginalCarSettings(const char* carname)
1135 {
1136 const int BUFSIZE = 1024;
1137 char buf[BUFSIZE];
1138
1139 // Fetch car handle
1140 snprintf(buf, BUFSIZE, "%scars/%s/%s.xml", GetDataDir(), carname, carname);
1141 void* carhdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
1142 if (carhdle == 0) {
1143 GfError("carhdle NULL in %s, line %d\n", __FILE__, __LINE__);
1144 return NULL;
1145 }
1146
1147 // Get category
1148 const char* category = GfParmGetStr(carhdle, SECT_CAR, PRM_CATEGORY, NULL);
1149 if (category == 0) {
1150 GfError("category string NULL in %s, line %d\n", __FILE__, __LINE__);
1151 GfParmReleaseHandle(carhdle);
1152 return NULL;
1153 }
1154
1155 // Fetch category handle
1156 snprintf(buf, BUFSIZE, "%scategories/%s.xml", GetDataDir(), category);
1157 void* cathdle = GfParmReadFile(buf, GFPARM_RMODE_STD);
1158 if (cathdle == 0) {
1159 GfError("cathdle NULL in %s, line %d\n", __FILE__, __LINE__);
1160 GfParmReleaseHandle(carhdle);
1161 return NULL;
1162 }
1163
1164 // Compose final result, MergeHandles releases source handles with given parameters
1165 cathdle = GfParmReadFile(buf, GFPARM_RMODE_STD | GFPARM_RMODE_CREAT);
1166 carhdle = GfParmMergeHandles(cathdle, carhdle, GFPARM_MMODE_SRC | GFPARM_MMODE_DST | GFPARM_MMODE_RELSRC | GFPARM_MMODE_RELDST);
1167
1168 return carhdle;
1169 }
1170
1171
1172 /** Initialize the given tCarPitSetup with the original TORCS setup, that means the car setup
1173 merged with the cars category setup
1174 @ingroup setuptools
1175 @param[in,out] s Pointer to tCarPitSetup struct to fill/initialize
1176 @param[in] carname TORCS internal name of the car (directory/filename)
1177 @return True on success, false on failure
1178 */
RtInitCarPitSetupFromDefault(tCarPitSetup * s,const char * carname)1179 bool RtInitCarPitSetupFromDefault(tCarPitSetup* s, const char* carname)
1180 {
1181 void* carhandle = RtLoadOriginalCarSettings(carname);
1182 if (carhandle == 0) {
1183 GfError("carhandle NULL in %s, line %d\n", __FILE__, __LINE__);
1184 return false;
1185 }
1186
1187 RtInitCarPitSetup(carhandle, s, false);
1188 GfParmReleaseHandle(carhandle);
1189 return true;
1190 }
1191
1192
1193 /** Load a custom car setup file for a given robot, car, track and session (race, practice, qualifying, ...) type.
1194 @ingroup setuptools
1195 @param[in] type Setup type
1196 @param[in] modulename Name of robot module without extension
1197 @param[in] robidx Index of robot
1198 @param[in] trackname TORCS internal name name of track
1199 @param[in] carname TORCS internal name car name
1200 @return Handle to data, or NULL on failure (e.g. if file is not available)
1201 @note Robot, car, track and session information are used to compose a standard setup filename
1202 */
RtParmReadSetup(rtCarPitSetupType type,const char * modulename,int robidx,const char * trackname,const char * carname)1203 void* RtParmReadSetup(
1204 rtCarPitSetupType type,
1205 const char* modulename,
1206 int robidx,
1207 const char* trackname,
1208 const char* carname
1209 )
1210 {
1211 const int filelen = 256;
1212 char filename[filelen];
1213 RtGetCarPitSetupFilename(type, robidx, carname, trackname, filename, filelen);
1214
1215 const int pathlen = 1024;
1216 char path[pathlen];
1217
1218 snprintf(path, pathlen, "%sdrivers/%s/setups/%s.xml", GetLocalDir(), modulename, filename);
1219 return GfParmReadFile(path, GFPARM_RMODE_STD);
1220 }
1221