1
1
import sys
2
2
import os
3
3
import time
4
+ import bisect
4
5
5
6
import ac
6
7
import acsys
18
19
from LeaderboardRow import LeaderboardRow
19
20
from FastestLapBanner import FastestLapBanner
20
21
22
+
23
+ MAX_LAP_TIME = 999999999
24
+
21
25
# TIMERS
22
26
timer0 , timer1 , timer2 = 0 , 0 , 0
27
+
23
28
24
29
# VARIABLES
25
30
totalDrivers = 0
26
31
drivers = None
27
- fastest_lap = 99999999
32
+ fastest_lap = MAX_LAP_TIME
28
33
29
34
race_started = False
30
35
replay_started = False
31
36
32
37
# REPLAY FILE
33
38
replay_file = None
39
+ replay_data = None
34
40
35
41
# WINDOWS
36
42
leaderboardWindow = None
@@ -54,6 +60,8 @@ def __init__(self, id, n_splits):
54
60
self .pit_time = 0
55
61
self .tyre = ""
56
62
self .timeDiff = 0
63
+ self .out = False
64
+ self .current_lap = 0
57
65
58
66
def get_split_id (self , spline ):
59
67
return int (spline // (1 / self .n_splits ))
@@ -139,6 +147,7 @@ def acUpdate(deltaT):
139
147
global race_started , replay_started
140
148
141
149
global replay_file
150
+ global replay_data
142
151
143
152
# Widgets
144
153
global leaderboardWindow , driverWidget
@@ -168,7 +177,9 @@ def acUpdate(deltaT):
168
177
169
178
# ============================
170
179
# SERVER LAP
171
- lc = max ((ac .getCarState (i , acsys .CS .LapCount ) for i in range (totalDrivers ))) + 1
180
+ for i in range (totalDrivers ):
181
+ drivers [i ].current_lap = ac .getCarState (i , acsys .CS .LapCount )
182
+ lc = max ((drivers [i ].current_lap for i in range (totalDrivers ))) + 1
172
183
if lc >= info .graphics .numberOfLaps :
173
184
ac .setText (lapCountTimerLabel , "FINAL LAP" )
174
185
ac .setFontColor (lapCountTimerLabel , 1 ,0 ,0 ,1 )
@@ -183,22 +194,22 @@ def acUpdate(deltaT):
183
194
driver_ahead , driver = dPosition [i - 1 ], dPosition [i ]
184
195
timeDiff = driver .split_times [driver .current_split - 1 ] - driver_ahead .split_times [driver .current_split - 1 ]
185
196
if timeDiff < 0 : continue # ignore these times, happens on overtakes
197
+ if driver .position > totalDrivers : continue # might try to update before it is possible
186
198
driver .timeDiff = timeDiff
187
- if driver .position > totalDrivers : continue # might try to update befor it is possible
188
199
leaderboard [driver .position ].update_time ("+" + time_to_string (timeDiff * 1000 ))
189
200
leaderboard [0 ].update_time ("Interval" ) # Force it
190
201
191
202
# ============================
192
203
# MARK FASTEST LAP
193
204
if lc > FC .FASTEST_LAP_STARTING_LAP :
194
- fastest_driver = None
195
205
for d in drivers :
196
206
lap = ac .getCarState (d .id , acsys .CS .BestLap )
197
207
if lap != 0 and lap < fastest_lap :
198
208
fastest_lap = lap
199
209
fastest_lap_banner .show (lap , ac .getDriverName (d .id ))
200
- fastest_driver = d
201
210
LeaderboardRow .FASTEST_LAP_ID = d .id ;
211
+ if replay_file :
212
+ write_fastest_lap (replay_file , info .graphics .completedLaps , info .graphics .iCurrentTime , d , fastest_lap )
202
213
for row in leaderboard :
203
214
row .mark_fastest_lap ()
204
215
@@ -216,14 +227,17 @@ def acUpdate(deltaT):
216
227
217
228
# ============================
218
229
# CHANGE CAR FOCUS AND DRIVER WIDGET
219
- id = ac .getFocusedCar ()
220
- if drivers [id ].position <= totalDrivers : # in case it wasnt updated yet
221
- driverWidget .show (id , drivers [id ].position , drivers [id ].starting_position , drivers [id ].tyre , drivers [id ].pits )
230
+ if race_started :
231
+ id = ac .getFocusedCar ()
232
+ if drivers [id ].position <= totalDrivers : # in case it wasnt updated yet
233
+ driverWidget .show (id , drivers [id ].position , drivers [id ].starting_position , drivers [id ].tyre , drivers [id ].pits )
234
+ else :
235
+ driverWidget .hide ()
222
236
223
237
# ========================================================
224
238
# SAVE DRIVER STATUS IN A FILE TO LOAD ON REPLAY
225
239
if replay_file :
226
- write_driver_info (replay_file , info .graphics .iCurrentTime , drivers )
240
+ write_driver_info (replay_file , info .graphics .completedLaps , info . graphics . iCurrentTime , drivers )
227
241
# ========================================================
228
242
229
243
# 3 times per second
@@ -234,16 +248,20 @@ def acUpdate(deltaT):
234
248
for d in drivers :
235
249
d .starting_position = ac .getCarLeaderboardPosition (d .id )
236
250
d .tyre = ac .getCarTyreCompound (d .id )
237
- replay_file = setup_replay_file (drivers ) # save starting info on the drivers
251
+ replay_file = setup_replay_file (drivers , info . graphics . numberOfLaps ) # save starting info on the drivers
238
252
239
253
# ============================
240
254
# POSITION UPDATE
241
255
for i in range (totalDrivers ):
242
256
pos = ac .getCarRealTimeLeaderboardPosition (i )
243
- if ac .isConnected (i ) == 0 : # mark unconnected drivers
257
+ connected = ac .isConnected (i )
258
+ if connected == 0 and not drivers [i ].out : # mark unconnected drivers
244
259
leaderboard [pos ].mark_out ()
245
- else :
260
+ drivers [i ].out = True
261
+ elif connected == 1 and drivers [i ].out :
246
262
leaderboard [pos ].mark_in ()
263
+ drivers [i ].out = False
264
+ if connected == 1 :
247
265
leaderboard [pos ].update_name (i )
248
266
249
267
# OVERTAKE
@@ -303,20 +321,86 @@ def acUpdate(deltaT):
303
321
ac .setBackgroundOpacity (driverWidget .window , 0 )
304
322
ac .setBackgroundOpacity (fastest_lap_banner .window , 0 )
305
323
306
- id = ac .getFocusedCar ()
307
- drivers [id ].position = 1
308
- driverWidget .show (id , drivers [id ].position , drivers [id ].starting_position , drivers [id ].tyre , drivers [id ].pits )
324
+ # ============================
325
+ # SERVER LAP
326
+ if replay_data :
327
+ lc = max ((drivers [i ].current_lap for i in range (totalDrivers ))) + 1
328
+ if lc >= replay_data ['nLaps' ]:
329
+ ac .setText (lapCountTimerLabel , "FINAL LAP" )
330
+ ac .setFontColor (lapCountTimerLabel , 1 ,0 ,0 ,1 )
331
+ else :
332
+ ac .setText (lapCountTimerLabel , "%d / %d" % (lc , replay_data ['nLaps' ]))
333
+ ac .setFontColor (lapCountTimerLabel , 0.86 , 0.86 , 0.86 , 1 )
334
+
335
+ # ============================
336
+ # PITS MARKER
337
+ for row in leaderboard :
338
+ if ac .isCarInPitline (row .driverId ) == 1 :
339
+ row .mark_enter_pits ()
340
+ else :
341
+ row .mark_left_pits ()
342
+
343
+ # ============================
344
+ # DRIVER WIDGET UPDATE
345
+ if replay_started :
346
+ id = ac .getFocusedCar ()
347
+ driverWidget .show (id , drivers [id ].position , drivers [id ].starting_position , drivers [id ].tyre , drivers [id ].pits )
348
+ else :
349
+ driverWidget .hide ()
350
+
351
+ # ============================
352
+ # UPDATE TIMES
353
+ if replay_data :
354
+ for row in leaderboard :
355
+ row .update_time ("+" + time_to_string (drivers [row .driverId ].timeDiff * 1000 ))
356
+ if row .row == 0 :
357
+ row .update_time ("Interval" ) # Force it
309
358
310
359
if timer1 > 0.3 :
311
360
if not replay_started :
312
361
if info .graphics .iCurrentTime > 0 :
313
- replay_file = open_replay_file_read ( )
362
+ replay_data = load_replay_file ( drivers )
314
363
replay_started = True
315
- for d in drivers :
316
- d .starting_position = ac .getCarLeaderboardPosition (d .id )
317
- d .tyre = ac .getCarTyreCompound (d .id )
318
364
319
-
365
+ # ============================
366
+ # FASTEST LAP BANNER TIMER
367
+ if fastest_lap_banner .timer > 0 :
368
+ fastest_lap_banner .timer -= timer1
369
+ fastest_lap_banner .hide ()
370
+
371
+ # ============================
372
+ # GET DATA FOR THIS UPDATE
373
+ if replay_data :
374
+ new_positions = lookup_data (info .graphics .completedLaps , info .graphics .iCurrentTime , replay_data , drivers )
375
+
376
+ # ============================
377
+ # POSITION UPDATE
378
+ for i in range (totalDrivers ):
379
+ pos = new_positions [i ]
380
+ if drivers [i ].out : # mark unconnected drivers
381
+ leaderboard [pos ].mark_out ()
382
+ else :
383
+ leaderboard [pos ].mark_in ()
384
+ leaderboard [pos ].update_name (i )
385
+
386
+ # OVERTAKE
387
+ if pos != drivers [i ].position : # there was an overtake
388
+ drivers [i ].timer = FC .OVERTAKE_POSITION_LABEL_TIMER # set timer
389
+ if pos < drivers [i ].position :
390
+ leaderboard [pos ].mark_green_position ()
391
+ elif pos > drivers [i ].position :
392
+ leaderboard [pos ].mark_red_position ()
393
+ elif drivers [i ].timer <= 0 :
394
+ leaderboard [pos ].mark_white_position ()
395
+ else :
396
+ drivers [i ].timer -= timer1
397
+ drivers [i ].position = pos
398
+ # END OVERTAKE
399
+
400
+ timer1 = 0
401
+
402
+ # END UPDATE
403
+
320
404
def acShutdown ():
321
405
global replay_file
322
406
if replay_file :
@@ -325,23 +409,67 @@ def acShutdown():
325
409
def reset_variables ():
326
410
pass
327
411
328
- def write_driver_info (replay_file , time , drivers ):
329
- data = "%d " % time
412
+ def write_driver_info (replay_file , laps , time , drivers ):
413
+ data = "U %d %d " % ( laps , time )
330
414
for d in drivers :
331
- data += "[ %d;%f;%s;%d] " % (d .position , d .timeDiff , d .tyre , d .pits )
415
+ data += "%d;%f;%s;%d;%d;%d " % (d .position , d .timeDiff , d .tyre , d .pits , int ( d . out ), d . current_lap )
332
416
replay_file .write (data + '\n ' )
333
- pass
334
417
335
- def setup_replay_file (drivers ):
418
+ def write_fastest_lap (replay_file , laps , time , driver , fastest_lap ):
419
+ replay_file .write ("FL %d;%d %d;%f\n " % (laps , time , driver .id , fastest_lap ))
420
+
421
+ def setup_replay_file (drivers , nLaps ):
336
422
replay_file = open (FC .REPLAY_DIR + "replayFile.txt" , "w" )
337
- data = "START: "
423
+ data = "START %d %d " % ( len ( drivers ), nLaps )
338
424
for d in drivers :
339
- data += "[ %d;%s] " % (d .starting_position , d .tyre )
425
+ data += "%d;%s " % (d .starting_position , d .tyre )
340
426
replay_file .write (data + '\n ' )
341
427
return replay_file
342
428
343
- def load_replay_file ():
429
+ def load_replay_file (drivers ):
344
430
try :
345
- return open (FC .REPLAY_DIR + "replayFile.txt" , "r" )
431
+ with open (FC .REPLAY_DIR + "replayFile.txt" , "r" ) as rf :
432
+ data = {}
433
+ line = next (rf ).split ()
434
+ if line [0 ] != "START" :
435
+ ac .log ("Replay file doesnt start with 'START' tag." )
436
+ return
437
+ totalDrivers = int (line [1 ])
438
+ data ['totalDrivers' ] = totalDrivers
439
+ data ['nLaps' ] = int (line [2 ])
440
+ for i in range (3 , 3 + totalDrivers ):
441
+ line [i ] = line [i ].split (';' )
442
+ drivers [i - 3 ].starting_position = int (line [i ][0 ])
443
+ drivers [i - 3 ].tyre = line [i ][1 ]
444
+ for line in rf :
445
+ line = line .split ()
446
+ if line [0 ] == "U" : # normal update
447
+ update = [float (line [2 ])]
448
+ for i in range (3 , 3 + totalDrivers ):
449
+ line [i ] = line [i ].split (';' )
450
+ update .append ([int (line [i ][0 ]), float (line [i ][1 ]), line [i ][2 ], int (line [i ][3 ]), bool (int (line [i ][4 ])), int (line [i ][5 ])])
451
+ if int (line [1 ]) not in data :
452
+ data [int (line [1 ])] = []
453
+ data [int (line [1 ])].append (update )
454
+ elif line [0 ] == "FL" : # TODO fastest lap
455
+ pass
456
+ else :
457
+ ac .log ("Replay file has wrong tag '%s'." % line [0 ])
458
+ return
459
+ return data
460
+
346
461
except FileNotFoundError :
347
- ac .log ("Replay File not found." )
462
+ ac .log ("Replay File not found." )
463
+
464
+ def lookup_data (lap , time , replay_data , drivers ):
465
+ it = bisect .bisect_left (replay_data [lap ], [time ])
466
+ if it > len (replay_data [lap ]): return
467
+ data = replay_data [lap ][it ]
468
+ for i in range (len (drivers )):
469
+ drivers [i ].timeDiff = data [i + 1 ][1 ]
470
+ drivers [i ].tyre = data [i + 1 ][2 ]
471
+ drivers [i ].pits = data [i + 1 ][3 ]
472
+ drivers [i ].out = data [i + 1 ][4 ]
473
+ drivers [i ].current_lap = data [i + 1 ][5 ]
474
+ return [data [i + 1 ][0 ] for i in range (len (drivers ))] # return new positions
475
+
0 commit comments