]> www.fi.muni.cz Git - slotcarman.git/blob - SCX/Track.pm
6c4b6447cf41f0afd99c55b7f3cd2cfd4abe8239
[slotcarman.git] / SCX / Track.pm
1 #!/usr/bin/perl -w
2
3 package SCX::Track;
4
5 use strict;
6 use Carp;
7
8 use Time::HiRes qw(gettimeofday);
9 use Glib qw(TRUE FALSE);
10 use SCX::Car;
11
12 our $SEMAPHORE_STEP = 1000;
13
14 sub new {
15         my ($class, $args) = @_;
16
17         my $self = {
18                 gui => $args->{gui},
19                 race_running => 0,
20                 lap_counting_up => 1,
21                 round => 0,
22                 now => 0,
23                 qualification_setup => -100,
24         };
25
26         bless $self, $class;
27
28         for my $i (0..5) {
29                 $self->{cars}->[$i] = SCX::Car->new({
30                         gui => $self->{gui},
31                         id => $i,
32                         track => $self,
33                 });
34         }
35
36         $self->print_rounds;
37         $self->{gui}->time(undef);
38         $self->{gui}->best_lap(undef, undef);
39
40         return $self;
41 }
42
43 sub car { return shift->{cars}->[shift]; }
44
45 sub race_start {
46         my ($self, $time) = @_;
47
48         return if $self->{race_running} || $self->{start_in_progress}
49                 || $self->{qualification_running};
50
51         if ($time - $self->{qualification_setup} < 1) {
52                 $self->{qualification_running} = 1;
53         } else {
54                 $self->{round} = 0;
55                 $self->{race_running} = 0;
56                 $self->{start_in_progress} = 1;
57                 $self->{semaphore} = 0;
58                 $self->{gui}->show_semaphore(0);
59                 Glib::Timeout->add($SEMAPHORE_STEP, \&semaphore_step, $self);
60         }
61         $self->print_rounds;
62 }
63
64 sub semaphore_step {
65         my ($self) = @_;
66
67         return FALSE if !$self->{start_in_progress} && !$self->{race_running};
68
69         $self->{semaphore}++;
70         if ($self->{semaphore} <= 5) {
71                 $self->{gui}->show_semaphore($self->{semaphore});
72                 my $timeout = $SEMAPHORE_STEP;
73                 $timeout += $SEMAPHORE_STEP * rand()
74                         if $self->{semaphore} == 5;
75                 Glib::Timeout->add($timeout, \&semaphore_step, $self);
76         } elsif ($self->{semaphore} == 6) {
77                 $self->{race_running} = 1;
78                 $self->{race_running_since} = gettimeofday;
79                 $self->{start_in_progress} = undef;
80                 $self->{gui}->show_semaphore(0);
81                 Glib::Timeout->add($SEMAPHORE_STEP, \&semaphore_step, $self);
82         } else {
83                 $self->{gui}->show_semaphore(undef);
84                 $self->{semaphore} = undef;
85         }
86         return FALSE;
87 }
88
89 sub race_end {
90         my ($self) = @_;
91
92         $self->{race_running} = 0;
93 }
94
95 sub race_setup {
96         my ($self, $rounds, $time) = @_;
97
98         if ($time - $self->{qualification_setup} < 1) {
99                 $self->{round} = 0;
100         } else {
101                 if ($rounds) {
102                         $self->{race_rounds} = $rounds;
103                 } else {
104                         $self->{race_rounds} = 0;
105                 }
106         }
107
108         $self->{round} = 0;
109         $self->print_rounds;
110         $self->{best_lap} = undef;
111
112         $self->{gui}->show_semaphore(undef);
113         $self->{race_running} = 0;
114         $self->{qualification_running} = 0;
115         $self->{start_in_progress} = 0;
116
117         $self->{gui}->time(undef);
118         $self->{gui}->best_lap(undef);
119 }
120
121 sub reset {
122         my ($self) = @_;
123
124         $self->{race_running} = 0;
125         $self->{qualification_running} = 0;
126         $self->{start_in_progress} = 0;
127         $self->{race_finishing} = 0;
128         $self->{best_lap} = undef;
129         $self->{round} = 0;
130
131         $self->print_rounds;
132         $self->{gui}->best_lap(undef);
133         $self->{gui}->time(undef);
134
135         for my $car (0..5) {
136                 $self->car($car)->reset;
137         }
138 }
139
140 sub print_rounds {
141         my ($self) = @_;
142
143         my $msg;
144         if ($self->{qualification_running}
145                 || $self->{now} - $self->{qualification_setup} < 1) {
146                 $msg = 'Qualification: ' . $self->{race_rounds}
147                         . ($self->{race_rounds} == 1 ? ' round' : ' rounds');
148         } elsif ($self->{race_rounds}) {
149                 $msg = $self->{round} . '/' . $self->{race_rounds};
150         } else {
151                 $msg = $self->{round};
152         }
153
154         $self->{gui}->rounds($msg);
155 }
156
157 sub check_best_lap {
158         my ($self, $time, $who) = @_;
159
160         return if !defined $time || $time == 0;
161
162 #       print "Check_best_lap $time $who vs ",
163 #               defined $self->{best_lap} ? $self->{best_lap} : 'undef',
164 #               "\n";
165         if (!defined $self->{best_lap}
166                 || $time < $self->{best_lap}) {
167                 $self->{best_lap} = $time;
168                 $self->{gui}->best_lap($time, $who);
169                 return 1;
170         }
171         return 0;
172 }
173
174 sub qualification_setup {
175         my ($self, $rounds, $cars, $time) = @_;
176
177         return if $self->{qualification_running};
178
179         for my $car (0..5) {
180                 $self->car($car)->set_lap(undef);
181                 $self->car($car)->set_laptime(undef);
182         }
183
184         $self->{qualification_setup} = $time;
185         $self->{race_rounds} = $rounds;
186         $self->{qualification_cars} = $cars;
187         $self->{gui}->time(undef);
188         $self->{gui}->best_lap(undef);
189         $self->print_rounds;
190 }
191
192 sub packet_received {
193         my ($self, $time) = @_;
194
195         $self->{now} = $time;
196
197         if ($self->{race_running}) {
198                 $self->{gui}->time($time - $self->{race_running_since});
199         }
200 }
201
202 sub recalc_order {
203         my ($self, $now) = @_;
204
205         return if !$self->{race_running};
206
207         my @laps;
208         my @times;
209         for my $id (0..5) {
210                 $laps[$id]  = $self->car($id)->{lap} // -1;
211                 $times[$id] = $self->car($id)->{first_finish_time} // $now;
212         }
213
214         my @new_order = sort {
215                 $laps[$b] <=> $laps[$a]
216                 ||
217                 $times[$a] <=> $times[$b]
218                 ||
219                 $a <=> $b;
220         } (0..5);
221
222         my $lap_max = $laps[$new_order[0]];
223         my $lap_max_changed = 0;
224         if (defined $lap_max && defined $self->{round}
225                 && $lap_max != $self->{round}) {
226                 $self->{round} = $lap_max;
227                 $lap_max_changed = 1;
228                 $self->print_rounds;
229         }
230
231         if ($self->{round} && $self->{race_rounds}
232                 && $self->{round} > $self->{race_rounds}) {
233                 $self->{race_finishing} = 1;
234         }
235
236         for my $id (0..5) {
237                 my $car = $new_order[$id];
238                 if ($self->car($car)->{order} != $id) {
239                         $self->car($car)->set_order($id);
240                 }
241         }
242         return ($lap_max_changed, $lap_max, $times[$new_order[0]]);
243 }
244
245 sub recalc_qual_order {
246         my ($self) = @_;
247
248         return if !$self->{qualification_running};
249
250         my @times;
251         for my $id (0..5) {
252                 $times[$id] = $self->car($id)->{best_lap};
253                 if (!defined $times[$id] || $times[$id] <= 0) {
254                         $times[$id] = 999_999;
255                 }
256         }
257
258         my @new_order = sort {
259                 $times[$a] <=> $times[$b]
260                 ||
261                 $a <=> $b;
262         } (0..5);
263
264         my $best_time = $times[$new_order[0]];
265
266         for my $id (0..5) {
267                 my $car = $new_order[$id];
268                 if ($self->car($car)->{order} != $id) {
269                         $self->car($car)->set_order($id);
270                 }
271         }
272         return ($times[$new_order[0]]);
273 }
274
275 sub finish_line {
276         my ($self, $time, $regular, @cars) = @_;
277
278         my %processed;
279         my $was_processed;
280
281         for my $car (@cars) {
282                 if ($self->car($car)->finish_line($time, $regular)) {
283                         $processed{$car} = 1;
284                         $was_processed = 1;
285                 }
286         }
287
288         return if !$was_processed;
289
290         if ($self->{qualification_running}) {
291                 my ($best) = $self->recalc_qual_order;
292                 for my $car (0..5) {
293                         $self->car($car)->recalc_qual_distance($best);
294                 }
295                 return;
296         }
297
298         my ($lap_max_changed, $lap_max, $time_min)
299                 = $self->recalc_order($time);
300
301         for my $car (0..5) {
302                 if ($processed{$car}) {
303                         $self->car($car)->recalc_distance(
304                                 $lap_max, $time_min, $self->{race_finishing},
305                         );
306                 } elsif ($lap_max_changed) {
307                         $self->car($car)->greyout_distance;
308                 }
309         }
310 }
311
312 1;
313