TORCS  1.3.10
The Open Racing Car Simulator
Loading...
Searching...
No Matches
raceengine.cpp
Go to the documentation of this file.
1/***************************************************************************
2
3 file : raceengine.cpp
4 created : Sat Nov 23 09:05:23 CET 2002
5 copyright : (C) 2002-2026 by Eric EspiƩ, Bernhard Wymann
6 email : berniw@bluewin.ch
7
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
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <tgfclient.h>
25#include <robot.h>
26#include <raceman.h>
27#include <racescreens.h>
28#include <robottools.h>
29#include <portability.h>
30
31#include "racemain.h"
32#include "racegl.h"
33#include "raceinit.h"
34#include "racepit.h"
35#include "raceresults.h"
36
37#include "raceengine.h"
38
39static double msgDisp;
40static double bigMsgDisp;
41
43
44static void ReRaceRules(tCarElt *car);
45
46
47/* Compute Pit stop time */
48static void
50{
51 tSituation *s = ReInfo->s;
52 tReCarInfo *info = &(ReInfo->_reCarInfo[car->index]);
53
54 switch (car->_pitStopType) {
55 case RM_PIT_REPAIR:
57 info->totalPitTime = ReComputePitStopServiceTime(car, &(ReInfo->raceRules));
58
59 // Add time for tire change
60 if (car->pitcmd.tireChange == tCarPitCmd::ALL && car->info.skillLevel == 3 && ReInfo->raceRules.tireFactor > 0.0f) {
61 info->totalPitTime += ReInfo->raceRules.allTiresChangeTime;
62 }
63
64 if (ReInfo->s->raceInfo.type == RM_TYPE_PRACTICE || ReInfo->s->raceInfo.type == RM_TYPE_QUALIF) {
65 // Ensure that the right min/max values are in the setup structure (could have been modified by the robot))
66 RtInitCarPitSetup(car->_carHandle, &(car->pitcmd.setup), true);
67 } else {
68 // In case of the race no modifications are allowed, so completely reload the structure
69 RtInitCarPitSetup(car->_carHandle, &(car->pitcmd.setup), false);
70 }
71 car->_scheduledEventTime = s->currentTime + info->totalPitTime;
72 car->_penaltyTime = 0.0f;
73 ReInfo->_reSimItf.reconfig(car);
74 break;
76 info->totalPitTime = car->_penaltyTime;
77 car->_scheduledEventTime = s->currentTime + info->totalPitTime;
78 car->_penaltyTime = 0.0f;
79 break;
80 }
81}
82
83/* Return from interactive pit information */
84static void
86{
87 ReUpdtPitTime(car);
88 GfuiScreenActivate(ReInfo->_reGameScreen);
89}
90
91static void
93{
94 if (ReInfo->_reCurTime > msgDisp) {
95 ReSetRaceMsg("");
96 }
97 if (ReInfo->_reCurTime > bigMsgDisp) {
99 }
100}
101
102static void
103ReRaceMsgSet(const char *msg, double life)
104{
105 if ((ReInfo->_displayMode != RM_DISP_MODE_NONE) && (ReInfo->_displayMode != RM_DISP_MODE_CONSOLE)) {
106 ReSetRaceMsg(msg);
107 msgDisp = ReInfo->_reCurTime + life;
108 }
109}
110
111
112static void
113ReRaceBigMsgSet(const char *msg, double life)
114{
115 if ((ReInfo->_displayMode != RM_DISP_MODE_NONE) && (ReInfo->_displayMode != RM_DISP_MODE_CONSOLE)) {
116 ReSetRaceBigMsg(msg);
117 bigMsgDisp = ReInfo->_reCurTime + life;
118 }
119}
120
121
122static void
124{
125 int i, pitok;
126 tTrackSeg *sseg;
127 tdble wseg;
128 static float color[] = {0.5, 0.5, 1.0, 1.0};
129 tSituation *s = ReInfo->s;
130 const int BUFSIZE = 1024;
131 char buf[BUFSIZE];
132
133 tReCarInfo *info = &(ReInfo->_reCarInfo[car->index]);
134
135 if (car->_speed_x > car->_topSpeed) {
136 car->_topSpeed = car->_speed_x;
137 }
138
139 // For practice and qualif.
140 if (car->_speed_x > info->topSpd) {
141 info->topSpd = car->_speed_x;
142 }
143
144 if (car->_speed_x < info->botSpd) {
145 info->botSpd = car->_speed_x;
146 car->_currentMinSpeedForLap = car->_speed_x;
147 }
148
149 // Pitstop.
150 if (car->_pit) {
151 if (car->ctrl.raceCmd & RM_CMD_PIT_ASKED) {
152 // Pit already occupied?
153 if (car->_pit->pitCarIndex == TR_PIT_STATE_FREE) {
154 snprintf(car->ctrl.msg[2], 32, "Can Pit");
155 } else {
156 snprintf(car->ctrl.msg[2], 32, "Pit Occupied");
157 }
158
159 memcpy(car->ctrl.msgColor, color, sizeof(car->ctrl.msgColor));
160 }
161
162 if (car->_state & RM_CAR_STATE_PIT) {
163 car->ctrl.raceCmd &= ~RM_CMD_PIT_ASKED; // clear the flag.
164 if (car->_scheduledEventTime < s->currentTime) {
165 car->_state &= ~RM_CAR_STATE_PIT;
166 car->_pit->pitCarIndex = TR_PIT_STATE_FREE;
167 snprintf(buf, BUFSIZE, "%s pit stop %.1fs", car->_name, info->totalPitTime);
168 ReRaceMsgSet(buf, 5);
169 } else {
170 snprintf(car->ctrl.msg[2], 32, "in pits %.1fs", s->currentTime - info->startPitTime);
171 }
172 } else if ((car->ctrl.raceCmd & RM_CMD_PIT_ASKED) &&
173 car->_pit->pitCarIndex == TR_PIT_STATE_FREE &&
174 (s->_maxDammage == 0 || car->_dammage <= s->_maxDammage))
175 {
176 tdble lgFromStart = car->_trkPos.seg->lgfromstart;
177
178 switch (car->_trkPos.seg->type) {
179 case TR_STR:
180 lgFromStart += car->_trkPos.toStart;
181 break;
182 default:
183 lgFromStart += car->_trkPos.toStart * car->_trkPos.seg->radius;
184 break;
185 }
186
187 if ((lgFromStart > car->_pit->lmin) && (lgFromStart < car->_pit->lmax)) {
188 pitok = 0;
189 int side;
190 tdble toBorder;
191 if (ReInfo->track->pits.side == TR_RGT) {
192 side = TR_SIDE_RGT;
193 toBorder = car->_trkPos.toRight;
194 } else {
195 side = TR_SIDE_LFT;
196 toBorder = car->_trkPos.toLeft;
197 }
198
199 sseg = car->_trkPos.seg->side[side];
200 wseg = RtTrackGetWidth(sseg, car->_trkPos.toStart);
201 if (sseg->side[side]) {
202 sseg = sseg->side[side];
203 wseg += RtTrackGetWidth(sseg, car->_trkPos.toStart);
204 }
205 if (((toBorder + wseg) < (ReInfo->track->pits.width - car->_dimension_y / 2.0)) &&
206 (fabs(car->_speed_x) < 1.0) &&
207 (fabs(car->_speed_y) < 1.0))
208 {
209 pitok = 1;
210 }
211
212 if (pitok) {
213 car->_state |= RM_CAR_STATE_PIT;
214 car->_nbPitStops++;
215 for (i = 0; i < car->_pit->freeCarIndex; i++) {
216 if (car->_pit->car[i] == car) {
217 car->_pit->pitCarIndex = i;
218 break;
219 }
220 }
221 info->startPitTime = s->currentTime;
222 snprintf(buf, BUFSIZE, "%s in pits", car->_name);
223 ReRaceMsgSet(buf, 5);
225 if (car->robot->rbPitCmd(car->robot->index, car, s) == ROB_PIT_MENU) {
226 // the pit cmd is modified by menu.
227 ReStop();
228 RmPitMenuStartAction(car, ReInfo, [car] { ReUpdtPitCmd(car); });
229 } else {
230 ReUpdtPitTime(car);
231 }
232 }
233 }
234 }
235 }
236
237 /* Start Line Crossing */
238 if (info->prevTrkPos.seg != car->_trkPos.seg) {
239 if ((info->prevTrkPos.seg->raceInfo & TR_LAST) && (car->_trkPos.seg->raceInfo & TR_START)) {
240 if (info->lapFlag == 0) {
241 if ((car->_state & RM_CAR_STATE_FINISH) == 0) {
242 car->_laps++;
243 car->_remainingLaps--;
244 if (car->_laps > 1) {
245 car->_lastLapTime = s->currentTime - info->sTime;
246 car->_curTime += car->_lastLapTime;
247 if (car->_bestLapTime != 0) {
248 car->_deltaBestLapTime = car->_lastLapTime - car->_bestLapTime;
249 }
250 if ((car->_lastLapTime < car->_bestLapTime) || (car->_bestLapTime == 0)) {
251 if (car->_commitBestLapTime) {
252 car->_bestLapTime = car->_lastLapTime;
253 }
254 }
255
256 car->_commitBestLapTime = true;
257
258 if (car->_pos != 1) {
259 car->_timeBehindLeader = car->_curTime - s->cars[0]->_curTime;
260 car->_lapsBehindLeader = s->cars[0]->_laps - car->_laps;
261 car->_timeBehindPrev = car->_curTime - s->cars[car->_pos - 2]->_curTime;
262 s->cars[car->_pos - 2]->_timeBeforeNext = car->_timeBehindPrev;
263 } else {
264 car->_timeBehindLeader = 0;
265 car->_lapsBehindLeader = 0;
266 car->_timeBehindPrev = 0;
267
268 if (ReInfo->_displayMode == RM_DISP_MODE_CONSOLE) {
269 printf("Sim Time: %8.2f [s], Leader Laps: %4d, Leader Distance: %8.3f [km]\n", s->currentTime, car->_laps - 1, car->_distRaced/1000.0f);
270 }
271 }
272 info->sTime = s->currentTime;
273 switch (ReInfo->s->_raceType) {
274 case RM_TYPE_PRACTICE:
275 if (ReInfo->_displayMode == RM_DISP_MODE_NONE) {
276 ReInfo->_refreshDisplay = 1;
277 const int TIMEFMTSIZE=256;
278 char t1[TIMEFMTSIZE], t2[TIMEFMTSIZE];
279 GfTime2Str(t1, TIMEFMTSIZE, car->_lastLapTime, 0);
280 GfTime2Str(t2, TIMEFMTSIZE, car->_bestLapTime, 0);
281 snprintf(buf, BUFSIZE, "lap: %02d time: %s best: %s top spd: %.2f min spd: %.2f damage: %d",
282 car->_laps - 1, t1, t2,
283 info->topSpd * 3.6, info->botSpd * 3.6, car->_dammage);
285 }
286 /* save the lap result */
288 break;
289
290 case RM_TYPE_QUALIF:
291 if (ReInfo->_displayMode == RM_DISP_MODE_NONE) {
293 }
294 break;
295 }
296 } else {
297 if ((ReInfo->_displayMode == RM_DISP_MODE_NONE) && (ReInfo->s->_raceType == RM_TYPE_QUALIF)) {
299 }
300 }
301
302 info->topSpd = car->_speed_x;
303 info->botSpd = car->_speed_x;
304 car->_currentMinSpeedForLap = car->_speed_x;
305 if ((car->_remainingLaps < 0) || (s->_raceState == RM_RACE_FINISHING)) {
306 car->_state |= RM_CAR_STATE_FINISH;
307 s->_raceState = RM_RACE_FINISHING;
308 if (ReInfo->s->_raceType == RM_TYPE_RACE) {
309 if (car->_pos == 1) {
310 snprintf(buf, BUFSIZE, "Winner %s", car->_name);
311 ReRaceBigMsgSet(buf, 10);
312 } else {
313 const char *numSuffix = "th";
314 if (abs(12 - car->_pos) > 1) { /* leave suffix as 'th' for 11 to 13 */
315 switch (car->_pos % 10) {
316 case 1:
317 numSuffix = "st";
318 break;
319 case 2:
320 numSuffix = "nd";
321 break;
322 case 3:
323 numSuffix = "rd";
324 break;
325 default:
326 break;
327 }
328 }
329 snprintf(buf, BUFSIZE, "%s Finished %d%s", car->_name, car->_pos, numSuffix);
330 ReRaceMsgSet(buf, 5);
331 }
332 }
333 }
334 } else {
335 /* prevent infinite looping of cars around track, allow one lap after finish for the first car */
336 for (i = 0; i < s->_ncars; i++) {
337 s->cars[i]->_state |= RM_CAR_STATE_FINISH;
338 }
339 return;
340 }
341 } else {
342 info->lapFlag--;
343 }
344 }
345
346 if ((info->prevTrkPos.seg->raceInfo & TR_START) && (car->_trkPos.seg->raceInfo & TR_LAST)) {
347 /* going backward through the start line */
348 info->lapFlag++;
349 }
350 }
351 ReRaceRules(car);
352
353 info->prevTrkPos = car->_trkPos;
354 car->_curLapTime = s->currentTime - info->sTime;
355 car->_distFromStartLine = car->_trkPos.seg->lgfromstart +
356 (car->_trkPos.seg->type == TR_STR ? car->_trkPos.toStart : car->_trkPos.toStart * car->_trkPos.seg->radius);
357 car->_distRaced = (car->_laps - (info->lapFlag + 1)) * ReInfo->track->length + car->_distFromStartLine;
358}
359
360
361static void ReSortCars(void)
362{
363 int i, j;
364 tCarElt *car;
365 int allfinish;
366 tSituation *s = ReInfo->s;
367
368 if ((s->cars[0]->_state & RM_CAR_STATE_FINISH) == 0) {
369 allfinish = 0;
370 } else {
371 allfinish = 1;
372 }
373
374 for (i = 1; i < s->_ncars; i++) {
375 j = i;
376 while (j > 0) {
377 if ((s->cars[j]->_state & RM_CAR_STATE_FINISH) == 0) {
378 allfinish = 0;
379 if (s->cars[j]->_distRaced > s->cars[j-1]->_distRaced) {
380 car = s->cars[j];
381 s->cars[j] = s->cars[j-1];
382 s->cars[j-1] = car;
383 s->cars[j]->_pos = j+1;
384 s->cars[j-1]->_pos = j;
385 j--;
386 continue;
387 }
388 }
389 j = 0;
390 }
391 }
392
393 if (allfinish) {
394 ReInfo->s->_raceState = RM_RACE_ENDED;
395 }
396}
397
398
399/* Compute the race rules and penalties */
400static void
402{
403 tCarPenalty *penalty;
404 tTrack *track = ReInfo->track;
405 tRmCarRules *rules = &(ReInfo->rules[car->index]);
406 tTrackSeg *seg = RtTrackGetSeg(&(car->_trkPos));
407 tReCarInfo *info = &(ReInfo->_reCarInfo[car->index]);
408 tTrackSeg *prevSeg = RtTrackGetSeg(&(info->prevTrkPos));
409 static float color[] = {0.0, 0.0, 1.0, 1.0};
410
411 // DNF cars which need too much time for the current lap, this is mainly to avoid
412 // that a "hanging" driver can stop the quali from finishing.
413 // Allowed time is longest pitstop possible + time for tracklength with speed??? (currently fixed 10 [m/s]).
414 // for simplicity. Human driver is an exception to this rule, to allow explorers
415 // to enjoy the landscape.
416 // TODO: Make it configurable.
417 if ((car->_curLapTime > 84.5 + ReInfo->track->length/10.0) &&
418 (car->_driverType != RM_DRV_HUMAN))
419 {
420 car->_state |= RM_CAR_STATE_ELIMINATED;
421 return;
422 }
423
424 const int BUFSIZE = 1024;
425 char buf[BUFSIZE];
426
427 // Ignore some rules after the car has finished the race
428 if ((car->pub.state & RM_CAR_STATE_FINISH) == 0) {
429
430 // If a car hits the track wall the lap time is invalidated, because of tracks where this behaviour allows much faster laps (e.g. alpine-2)
431 // Invalidation and message is just shown on the first hit
432 if (ReInfo->raceRules.enabled & RmRaceRules::WALL_HIT_TIME_INVALIDATE) {
433 if (car->_commitBestLapTime && (car->priv.simcollision & SEM_COLLISION_XYSCENE)) {
434 car->_commitBestLapTime = false;
435 if (ReInfo->s->_raceType != RM_TYPE_RACE) {
436 ReRaceMsgSet("Hit wall, laptime invalidated", 5);
437 }
438 }
439 }
440
441 // If the car cuts a corner the lap time is invalidated. Cutting a corner means: the center of gravity is more than 0.7 times the car width
442 // away from the main track segment on the inside of a turn. The rule does not apply on the outside and on straights, pit entry and exit
443 // count as well as track.
444 tTrackSeg *mainseg = car->_trkPos.seg;
445 bool pit = false;
446 tTrackPitInfo pitInfo = track->pits;
447 tdble toborder = 0.0f;
448 tdble minradius = 1.0f;
449
450 if (mainseg->type != TR_STR) {
451 if (track->pits.type == TR_PIT_ON_TRACK_SIDE) {
452 if (pitInfo.pitEntry->id < pitInfo.pitExit->id) {
453 if ((mainseg->id >= pitInfo.pitEntry->id) && (mainseg->id <= pitInfo.pitExit->id)) {
454 pit = true;
455 }
456 } else {
457 if ((mainseg->id >= pitInfo.pitEntry->id) || (mainseg->id <= pitInfo.pitExit->id)) {
458 pit = true;
459 }
460 }
461 }
462
463 if (mainseg->type == TR_LFT) {
464 if (!(pit && (pitInfo.side == TR_LFT))) {
465 toborder = car->_trkPos.toLeft;
466 minradius = mainseg->radiusl;
467 }
468 } else if (mainseg->type == TR_RGT) {
469 if (!(pit && (pitInfo.side == TR_RGT))) {
470 toborder = car->_trkPos.toRight;
471 minradius = mainseg->radiusr;
472 }
473 }
474 }
475
476 tdble cuttinglimit = car->_dimension_y*0.7f;
477 if (toborder < -cuttinglimit) {
478 if (ReInfo->raceRules.enabled & RmRaceRules::CORNER_CUTTING_TIME_INVALIDATE) {
479 if (ReInfo->s->_raceType != RM_TYPE_RACE && car->_commitBestLapTime) {
480 ReRaceMsgSet("Cut corner, laptime invalidated", 5);
481 }
482 car->_commitBestLapTime = false;
483 }
484 if (ReInfo->s->_raceType == RM_TYPE_RACE && ReInfo->raceRules.enabled & RmRaceRules::CORNER_CUTTING_TIME_PENALTY) {
485 // In race, apply additionally corner cutting time penalty
486 minradius -= cuttinglimit;
487 if (minradius > 1.0f) {
488 car->_penaltyTime += car->pub.speed*RCM_MAX_DT_SIMU*(-toborder-cuttinglimit)/minradius;
489 }
490 }
491 }
492 }
493
494 if (car->_skillLevel < 3) {
495 /* only for the pros */
496 return;
497 }
498
499 penalty = GF_TAILQ_FIRST(&(car->_penaltyList));
500 if (penalty) {
501 if (car->_laps > penalty->lapToClear) {
502 /* too late to clear the penalty, out of race */
503 car->_state |= RM_CAR_STATE_ELIMINATED;
504 return;
505 }
506
507 switch (penalty->penalty) {
509 snprintf(car->ctrl.msg[3], 32, "Drive Through Penalty");
510 break;
512 snprintf(car->ctrl.msg[3], 32, "Stop And Go Penalty");
513 break;
514 default:
515 *(car->ctrl.msg[3]) = 0;
516 break;
517 }
518
519 memcpy(car->ctrl.msgColor, color, sizeof(car->ctrl.msgColor));
520 }
521
522 if (prevSeg->raceInfo & TR_PITSTART) {
523 /* just entered the pit lane */
524 if (seg->raceInfo & TR_PIT) {
525 /* may be a penalty can be cleaned up */
526 if (penalty) {
527 switch (penalty->penalty) {
529 snprintf(buf, BUFSIZE, "%s DRIVE THROUGH PENALTY CLEANING", car->_name);
530 ReRaceMsgSet(buf, 5);
532 break;
534 snprintf(buf, BUFSIZE, "%s STOP&GO PENALTY CLEANING", car->_name);
535 ReRaceMsgSet(buf, 5);
537 break;
538 }
539 }
540 }
541 } else if (prevSeg->raceInfo & TR_PIT) {
542 if (seg->raceInfo & TR_PIT) {
543 /* the car stopped in pits */
544 if (car->_state & RM_CAR_STATE_PIT) {
545 if (rules->ruleState & RM_PNST_DRIVETHROUGH) {
546 /* it's not more a drive through */
548 } else if (rules->ruleState & RM_PNST_STOPANDGO) {
550 }
551 } else {
552 if(rules->ruleState & RM_PNST_STOPANDGO_OK && car->_pitStopType != RM_PIT_STOPANDGO) {
554 }
555 }
556 } else if (seg->raceInfo & TR_PITEND) {
557 /* went out of the pit lane, check if the current penalty is cleared */
559 /* clear the penalty */
560 snprintf(buf, BUFSIZE, "%s penalty cleared", car->_name);
561 ReRaceMsgSet(buf, 5);
562 penalty = GF_TAILQ_FIRST(&(car->_penaltyList));
563 GF_TAILQ_REMOVE(&(car->_penaltyList), penalty, link);
564 FREEZ(penalty);
565 }
566
567 rules->ruleState = 0;
568 } else {
569 /* went out of the pit lane illegally... */
570 /* it's a new stop and go... */
571 if (!(rules->ruleState & RM_PNST_STNGO)) {
572 snprintf(buf, BUFSIZE, "%s STOP&GO PENALTY", car->_name);
573 ReRaceMsgSet(buf, 5);
574 penalty = (tCarPenalty*)calloc(1, sizeof(tCarPenalty));
575 penalty->penalty = RM_PENALTY_STOPANDGO;
576 penalty->lapToClear = car->_laps + 5;
577 GF_TAILQ_INSERT_TAIL(&(car->_penaltyList), penalty, link);
578 rules->ruleState = RM_PNST_STNGO;
579 }
580 }
581 } else if (seg->raceInfo & TR_PITEND) {
582 rules->ruleState = 0;
583 } else if (seg->raceInfo & TR_PIT) {
584 /* entrered the pits not from the pit entry... */
585 /* it's a new stop and go... */
586 if (!(rules->ruleState & RM_PNST_STNGO)) {
587 snprintf(buf, BUFSIZE, "%s STOP&GO PENALTY", car->_name);
588 ReRaceMsgSet(buf, 5);
589 penalty = (tCarPenalty*)calloc(1, sizeof(tCarPenalty));
590 penalty->penalty = RM_PENALTY_STOPANDGO;
591 penalty->lapToClear = car->_laps + 5;
592 GF_TAILQ_INSERT_TAIL(&(car->_penaltyList), penalty, link);
593 rules->ruleState = RM_PNST_STNGO;
594 }
595 }
596
597 if (seg->raceInfo & TR_SPEEDLIMIT) {
598 if (!(rules->ruleState & (RM_PNST_SPD | RM_PNST_STNGO)) && (fabs(car->_speed_x) > track->pits.speedLimit)) {
599 snprintf(buf, BUFSIZE, "%s DRIVE THROUGH PENALTY", car->_name);
600 ReRaceMsgSet(buf, 5);
601 rules->ruleState |= RM_PNST_SPD;
602 penalty = (tCarPenalty*)calloc(1, sizeof(tCarPenalty));
604 penalty->lapToClear = car->_laps + 5;
605 GF_TAILQ_INSERT_TAIL(&(car->_penaltyList), penalty, link);
606 }
607 }
608}
609
610static bool
611isDriverUpdateDue(int driverType, double currentTime, tRaceEngineInfo* raceEngineInfo)
612{
613 double updateInterval = driverType == RM_DRV_HUMAN ? RCM_MAX_DT_HUMAN : RCM_MAX_DT_ROBOTS;
614 double lastTime = raceEngineInfo->lastDriveTime[driverType];
615 return (currentTime - lastTime) >= updateInterval;
616}
617
618static void
619resetDriverUpdateTimers(double lastTime, tRaceEngineInfo* raceEngineInfo)
620{
621 raceEngineInfo->lastDriveTime[RM_DRV_HUMAN] = lastTime;
622 raceEngineInfo->lastDriveTime[RM_DRV_ROBOT] = lastTime;
623 raceEngineInfo->lastTime = lastTime;
624}
625
626static void
628{
629 bool driverUpdateDue[RM_DRV_ROBOT + 1] = { 0 };
630 driverUpdateDue[RM_DRV_HUMAN] = isDriverUpdateDue(RM_DRV_HUMAN, s->currentTime, raceEngineInfo);
631 driverUpdateDue[RM_DRV_ROBOT] = isDriverUpdateDue(RM_DRV_ROBOT, s->currentTime, raceEngineInfo);
632
633 if (driverUpdateDue[RM_DRV_HUMAN]) {
635 }
636
637 if (driverUpdateDue[RM_DRV_ROBOT]) {
640 }
641
642 for (int i = 0; i < s->_ncars; i++) {
643 if ((s->cars[i]->_state & RM_CAR_STATE_NO_SIMU) == 0 && driverUpdateDue[s->cars[i]->_driverType]) {
644 tRobotItf* robot = s->cars[i]->robot;
645 robot->rbDrive(robot->index, s->cars[i], s);
646 }
647 }
648
649 if (driverUpdateDue[RM_DRV_HUMAN]) {
650 raceEngineInfo->lastDriveTime[RM_DRV_HUMAN] = s->currentTime;
651 }
652
653 if (driverUpdateDue[RM_DRV_ROBOT]) {
654 raceEngineInfo->lastDriveTime[RM_DRV_ROBOT] = s->currentTime;
655 raceEngineInfo->lastTime = s->currentTime;
656 }
657}
658
659static void
660ReOneStep(double deltaTimeIncrement)
661{
662 int i;
663 tSituation *s = ReInfo->s;
664
665 if ((ReInfo->_displayMode != RM_DISP_MODE_NONE) && (ReInfo->_displayMode != RM_DISP_MODE_CONSOLE)) {
666 if (floor(s->currentTime) == -2.0) {
667 ReRaceBigMsgSet("Ready", 1.0);
668 } else if (floor(s->currentTime) == -1.0) {
669 ReRaceBigMsgSet("Set", 1.0);
670 } else if (floor(s->currentTime) == 0.0) {
671 ReRaceBigMsgSet("Go", 1.0);
672 }
673 }
674
675 ReInfo->_reCurTime += deltaTimeIncrement * ReInfo->_reTimeMult; /* "Real" time */
676 s->currentTime += deltaTimeIncrement; /* Simulated time */
677
678 if (s->currentTime < 0) {
679 /* no simu yet */
680 ReInfo->s->_raceState = RM_RACE_PRESTART;
681 } else if (ReInfo->s->_raceState == RM_RACE_PRESTART) {
682 ReInfo->s->_raceState = RM_RACE_RUNNING;
683 s->currentTime = 0.0; /* resynchronize */
684 resetDriverUpdateTimers(0.0, &ReInfo->raceEngineInfo);
685 }
686
687 START_PROFILE("rbDrive*");
688 runDueDriverCallbacks(s, &ReInfo->raceEngineInfo);
689 STOP_PROFILE("rbDrive*");
690
691 START_PROFILE("_reSimItf.update*");
692 ReInfo->_reSimItf.update(s, deltaTimeIncrement, -1);
693 for (i = 0; i < s->_ncars; i++) {
694 ReManage(s->cars[i]);
695 }
696 STOP_PROFILE("_reSimItf.update*");
697
698 if ((ReInfo->_displayMode != RM_DISP_MODE_NONE) && (ReInfo->_displayMode != RM_DISP_MODE_CONSOLE)) {
700 }
701 ReSortCars();
702}
703
704void
706{
707 ReInfo->_reRunning = 1;
708 ReInfo->_reCurTime = GfTimeClock() - RCM_MAX_DT_SIMU;
709}
710
711void
713{
714 ReInfo->_reGraphicItf.muteformenu();
715 ReInfo->_reRunning = 0;
716}
717
718static void
720{
721 unsigned char *img;
722 int sw, sh, vw, vh;
723 tRmMovieCapture *capture = &(ReInfo->movieCapture);
724 const int BUFSIZE = 1024;
725 char buf[BUFSIZE];
726
727 GfScrGetSize(&sw, &sh, &vw, &vh);
728 img = (unsigned char*)malloc(vw * vh * 3);
729 if (img == NULL) {
730 return;
731 }
732
733 glPixelStorei(GL_PACK_ROW_LENGTH, 0);
734 glPixelStorei(GL_PACK_ALIGNMENT, 1);
735 glReadBuffer(GL_FRONT);
736 glReadPixels((sw-vw)/2, (sh-vh)/2, vw, vh, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)img);
737
738 snprintf(buf, BUFSIZE, "%s/torcs-%4.4d-%8.8d.png", capture->outputBase, capture->currentCapture, capture->currentFrame++);
739 GfImgWritePngNoGamma(img, buf, vw, vh);
740 free(img);
741}
742
743
744int
746{
747 double t;
748 tRmMovieCapture *capture;
749 int mode = RM_ASYNC;
750 int i;
751 const int MAXSTEPS = 2000;
752
753 START_PROFILE("ReUpdate");
754 ReInfo->_refreshDisplay = 0;
755 switch (ReInfo->_displayMode) {
757 t = GfTimeClock();
758
759 i = 0;
760 START_PROFILE("ReOneStep*");
761 while ((ReInfo->_reRunning && ((t - ReInfo->_reCurTime) > RCM_MAX_DT_SIMU)) && MAXSTEPS > i++) {
763 }
764 STOP_PROFILE("ReOneStep*");
765
766 if (i > MAXSTEPS) {
767 // Cannot keep up with time warp, reset time to avoid lag when running slower again
768 ReInfo->_reCurTime = GfTimeClock();
769 }
770
771 GfuiDisplay();
772 ReInfo->_reGraphicItf.refresh(ReInfo->s);
773 glutPostRedisplay(); /* Callback -> reDisplay */
774 break;
775
777 // Just update view once per 2 seconds simulation time to avoid trouble with graphics cards
778 // which are bad in buffer switching (e.g. ATI fglrx driver on Linux).
779 t = ReInfo->_reCurTime;
780 while ((t - ReInfo->_reCurTime + 2.0) > 0.0) {
782 }
783
784 GfuiDisplay();
785 glutPostRedisplay(); /* Callback -> reDisplay */
786 break;
787
789 capture = &(ReInfo->movieCapture);
790 while ((ReInfo->_reCurTime - capture->lastFrame) < capture->deltaFrame) {
791 ReOneStep(capture->deltaSimu);
792 }
793 capture->lastFrame = ReInfo->_reCurTime;
794
795 GfuiDisplay();
796 ReInfo->_reGraphicItf.refresh(ReInfo->s);
797 reCapture();
798 glutPostRedisplay(); /* Callback -> reDisplay */
799 break;
800
802 t = ReInfo->_reCurTime;
803 while ((t - ReInfo->_reCurTime + 2.0) > 0.0) {
805 }
806 mode = RM_SYNC;
807 break;
808
809 }
810 STOP_PROFILE("ReUpdate");
811
812 return mode;
813}
814
815void
816ReTimeMod (int cmd)
817{
818 switch ((int)cmd) {
819 case 0:
820 ReInfo->_reTimeMult *= 2.0;
821 if (ReInfo->_reTimeMult > 64.0) {
822 ReInfo->_reTimeMult = 64.0;
823 }
824 break;
825 case 1:
826 ReInfo->_reTimeMult *= 0.5;
827 if (ReInfo->_reTimeMult < 1.0f/128.0f) {
828 ReInfo->_reTimeMult = 1.0f/128.0f;
829 }
830 break;
831 case 2:
832 default:
833 ReInfo->_reTimeMult = 1.0;
834 break;
835 }
836
837 const int BUFSIZE = 1024;
838 char buf[BUFSIZE];
839
840 snprintf(buf, BUFSIZE, "Time x%.2f", 1.0 / ReInfo->_reTimeMult);
841 ReRaceMsgSet(buf, 5);
842}
#define _pit
Definition car.h:184
#define RM_PENALTY_DRIVETHROUGH
Definition car.h:126
#define RM_PIT_STOPANDGO
Definition car.h:441
#define RM_CMD_PIT_ASKED
Race command: Pit asked.
Definition car.h:354
#define RM_CAR_STATE_PIT
Car currently stopped in pits.
Definition car.h:211
#define RM_DRV_HUMAN
Definition car.h:122
#define RM_CAR_STATE_NO_SIMU
Do not simulate the car.
Definition car.h:217
#define RM_DRV_ROBOT
Definition car.h:123
#define RM_PIT_REPAIR
Definition car.h:440
#define SEM_COLLISION_XYSCENE
Definition car.h:695
struct CarElt tCarElt
Car structure (tCarElt).
#define RM_CAR_STATE_FINISH
Car having passed the finish line.
Definition car.h:210
struct CarPenalty tCarPenalty
One penalty.
#define RM_CAR_STATE_ELIMINATED
Eliminated due to rules infringement.
Definition car.h:220
#define RM_PENALTY_STOPANDGO
Definition car.h:127
float tdble
Floating point type used in TORCS.
Definition tgf.h:47
void GfuiDisplay(void)
Draw the active GUI screen and swap the GLUT buffers.
Definition gui.cpp:175
void GfuiScreenActivate(void *screen)
Activate a screen and make it current.
Definition gui.cpp:508
int GfImgWritePngNoGamma(unsigned char *img, const char *filename, int width, int height)
Write a framebuffer capture to a png image on disk without gamma metadata.
Definition img.cpp:292
void RmPitMenuStartAction(tCarElt *car, tRmInfo *reInfo, const tGfuiAction &callback)
Pit menu.
Definition pitmenu.cpp:255
struct RobotItf tRobotItf
Interface Structure for Robots.
void GfTime2Str(char *result, int resultSize, tdble sec, int sgn)
Convert a time in seconds (float) to an ascii string.
Definition tgf.cpp:192
void GfScrGetSize(int *scrw, int *scrh, int *vieww, int *viewh)
Get the screen and viewport sizes.
Definition screen.cpp:529
void RtInitCarPitSetup(void *carparmhandle, tCarPitSetup *setup, bool minmaxonly)
Initialize tCarPitSetup from data in parameter set given in handle hdle.
Definition rttrack.cpp:748
#define GF_TAILQ_INSERT_TAIL(head, elm, field)
Insert an element at the tail.
Definition tgf.h:510
#define GF_TAILQ_REMOVE(head, elm, field)
Remove an element.
Definition tgf.h:540
#define GF_TAILQ_FIRST(head)
First element of a TAILQ.
Definition tgf.h:463
struct trackSeg tTrackSeg
Track segment (tTrackSeg) The segments can be straights (type TR_STR): (the track goes from the right...
tTrackSeg * RtTrackGetSeg(tTrkLocPos *p)
Get the effective segment of the given position starting from a segment of type TR_MAIN,...
Definition rttrack.cpp:450
tdble RtTrackGetWidth(tTrackSeg *seg, tdble toStart)
Get the track width at the specified point.
Definition rttrack.cpp:59
double GfTimeClock(void)
Get the time in seconds.
Definition os.cpp:50
static void ReRaceBigMsgSet(const char *msg, double life)
int ReUpdate(void)
static void ReRaceMsgUpdate(void)
tRmInfo * ReInfo
static void ReUpdtPitCmd(tCarElt *car)
static void ReOneStep(double deltaTimeIncrement)
static void ReUpdtPitTime(tCarElt *car)
static bool isDriverUpdateDue(int driverType, double currentTime, tRaceEngineInfo *raceEngineInfo)
static void ReSortCars(void)
static double msgDisp
static void runDueDriverCallbacks(tSituation *s, tRaceEngineInfo *raceEngineInfo)
static void ReRaceRules(tCarElt *car)
static void reCapture(void)
static double bigMsgDisp
void ReStart(void)
static void resetDriverUpdateTimers(double lastTime, tRaceEngineInfo *raceEngineInfo)
static void ReRaceMsgSet(const char *msg, double life)
void ReTimeMod(int cmd)
void ReStop(void)
static void ReManage(tCarElt *car)
void ReSetRaceBigMsg(const char *msg)
Definition racegl.cpp:151
void ReResScreenAddText(char *text)
Definition racegl.cpp:364
void ReSetRaceMsg(const char *msg)
Definition racegl.cpp:135
#define RM_RACE_PRESTART
Definition raceman.h:67
#define RCM_MAX_DT_ROBOTS
Definition raceman.h:56
#define RM_RACE_FINISHING
Definition raceman.h:64
struct RmInfo tRmInfo
Race Manager General Info.
#define RM_PNST_STNGO
Definition raceman.h:178
#define RCM_MAX_DT_HUMAN
Definition raceman.h:55
#define RCM_MAX_DT_SIMU
Definition raceman.h:54
struct RmMovieCapture tRmMovieCapture
#define RM_TYPE_PRACTICE
Definition raceman.h:70
#define RM_TYPE_RACE
Definition raceman.h:72
#define RM_ASYNC
Definition raceman.h:41
#define RM_PNST_STOPANDGO
Definition raceman.h:175
struct RmCarRules tRmCarRules
#define RM_PNST_DRIVETHROUGH
Definition raceman.h:174
#define RM_PNST_STOPANDGO_OK
Definition raceman.h:176
#define RM_DISP_MODE_CONSOLE
Definition raceman.h:150
#define RM_SYNC
Definition raceman.h:40
#define RM_DISP_MODE_CAPTURE
Definition raceman.h:148
#define RM_PNST_SPD
Definition raceman.h:177
#define RM_DISP_MODE_NONE
Definition raceman.h:149
#define RM_TYPE_QUALIF
Definition raceman.h:71
struct Situation tSituation
cars situation used to inform the GUI and the drivers
#define RM_DISP_MODE_NORMAL
Definition raceman.h:147
#define RM_RACE_RUNNING
Definition raceman.h:63
#define RM_RACE_ENDED
Definition raceman.h:65
tdble ReComputePitStopServiceTime(const tCarElt *car, const tRmRaceRules *raceRules)
Definition racepit.cpp:57
void ReNormalizePitStop(tCarElt *car)
Definition racepit.cpp:28
Pit stop command normalization and timing helpers.
void ReUpdateQualifCurRes(tCarElt *car)
void ReSavePracticeLap(tCarElt *car)
#define ROB_PIT_MENU
Call the interactive menu for pit command.
Definition robot.h:99
struct RobotItf * robot
private
Definition car.h:466
tPrivCar priv
private
Definition car.h:463
tPublicCar pub
public
Definition car.h:461
tCarPitCmd pitcmd
private
Definition car.h:465
tCarCtrl ctrl
private
Definition car.h:464
int index
car index
Definition car.h:459
tInitCar info
public
Definition car.h:460
int penalty
penalty type
Definition car.h:132
int lapToClear
the lap before the penalty has to be cleared
Definition car.h:133
int ruleState
Definition raceman.h:200
double deltaFrame
Definition raceman.h:208
const char * outputBase
Definition raceman.h:210
double deltaSimu
Definition raceman.h:207
int currentCapture
Definition raceman.h:211
double lastFrame
Definition raceman.h:209
@ CORNER_CUTTING_TIME_INVALIDATE
Definition raceman.h:183
@ CORNER_CUTTING_TIME_PENALTY
Definition raceman.h:185
@ WALL_HIT_TIME_INVALIDATE
Definition raceman.h:184
tfRbPitCmd rbPitCmd
Definition robot.h:110
int index
Definition robot.h:112
tfRbDrive rbDrive
Definition robot.h:109
tCarElt ** cars
list of cars
Definition raceman.h:89
double deltaTime
Legacy rbDrive delta for existing robot modules; normal robots use this value.
Definition raceman.h:86
double driveDeltaTime[RM_DRV_ROBOT+1]
rbDrive delta indexed by RM_DRV_*; index 0 is unused.
Definition raceman.h:90
double currentTime
current time in sec since the beginning of the simulation
Definition raceman.h:87
float msgColor[4]
RGBA of text.
Definition car.h:357
int raceCmd
command issued by the driver
Definition car.h:352
char msg[4][32]
4 lines of 31 characters 0-1 from car 2-3 from race engine
Definition car.h:355
@ ALL
Definition car.h:445
TireChange tireChange
Definition car.h:447
tCarPitSetup setup
Definition car.h:443
int skillLevel
Driver's skill level (0=rookie -> 3=pro)
Definition car.h:81
int simcollision
For rules etc.
Definition car.h:309
int state
state of the car.
Definition car.h:197
tdble speed
Definition car.h:194
Race Engine Information.
Definition raceman.h:132
double lastDriveTime[RM_DRV_ROBOT+1]
Last time rbDrive call indexed by RM_DRV_*; index 0 is unused.
Definition raceman.h:144
double lastTime
Legacy last time rbDrive call for existing robot modules.
Definition raceman.h:143
Race Engine Car Information about the race.
Definition raceman.h:116
tdble topSpd
Definition raceman.h:123
tTrkLocPos prevTrkPos
Definition raceman.h:117
double totalPitTime
Definition raceman.h:121
int lapFlag
Definition raceman.h:119
tdble botSpd
Definition raceman.h:124
tdble sTime
Definition raceman.h:118
double startPitTime
Definition raceman.h:122
Track structure.
Definition track.h:533
Pits Info Structure.
Definition track.h:469
tTrackSeg * pitEntry
Pit lane segment.
Definition track.h:486
tTrackSeg * pitExit
Pit lane segment.
Definition track.h:489
int side
Pits side:
Definition track.h:479
tTrackSeg * seg
Track segment.
Definition track.h:435
int type
Geometrical type:
Definition track.h:297
tdble radiusr
Radius in meters of the right side of the track (>0)
Definition track.h:338
unsigned int raceInfo
Type of segment regarding the race: Mask value in:
Definition track.h:382
int id
Segment number.
Definition track.h:295
tdble radiusl
Radius in meters of the left side of the track (>0)
Definition track.h:339
struct trackSeg * side[2]
Definition track.h:422
#define STOP_PROFILE(a)
Definition tgf.h:597
#define START_PROFILE(a)
Definition tgf.h:596
#define FREEZ(x)
Definition tgf.h:59
#define TR_SIDE_RGT
Definition track.h:419
#define TR_START
Segment after start line.
Definition track.h:395
#define TR_LAST
Segment before start line.
Definition track.h:394
#define TR_PITEND
Car pit End.
Definition track.h:402
#define TR_PITSTART
Car pit Star.
Definition track.h:401
#define TR_PIT_STATE_FREE
Definition track.h:459
#define TR_SPEEDLIMIT
Segment where the speed is limited.
Definition track.h:397
#define TR_LFT
Left curve.
Definition track.h:303
#define TR_PIT_ON_TRACK_SIDE
The pits are on the track side.
Definition track.h:475
#define TR_SIDE_LFT
Definition track.h:418
#define TR_STR
Straight.
Definition track.h:304
#define TR_PIT
Car pit.
Definition track.h:400
#define TR_RGT
Right curve.
Definition track.h:302
int track(tModInfo *modInfo)
Definition trackitf.cpp:85