#!/usr/bin/perl -w package SCX::Track; use strict; use Carp; use Glib qw(TRUE FALSE); use SCX::Car; use SCX::Sound; our $SEMAPHORE_STEP = 1000; sub new { my ($class, $args) = @_; my $self = { gui => $args->{gui}, race_running => 0, lap_counting_up => 1, round => 0, now => 0, qualification_setup => -100, no_semaphore => $args->{no_semaphore}, }; bless $self, $class; for my $i (0..5) { $self->{cars}->[$i] = SCX::Car->new({ gui => $self->{gui}, id => $i, track => $self, }); } $self->print_rounds; $self->{gui}->time(undef); $self->{gui}->best_lap(undef, undef); $self->{sound} = new SCX::Sound; return $self; } sub car { return shift->{cars}->[shift]; } sub race_start { my ($self, $time) = @_; return if $self->{race_running} || $self->{start_in_progress} || $self->{qualification_running}; if ($time - $self->{qualification_setup} < 1) { $self->{qualification_running} = 1; } elsif ($self->{no_semaphore}) { $self->{round} = 0; $self->{race_running} = 1; $self->{race_running_since} = $self->{now}; $self->{start_in_progress} = undef; } else { $self->{round} = 0; $self->{race_running} = 0; $self->{start_in_progress} = 1; $self->{semaphore} = 0; $self->{gui}->show_semaphore(0); Glib::Timeout->add($SEMAPHORE_STEP, \&semaphore_step, $self); } $self->print_rounds; } sub semaphore_step { my ($self) = @_; return FALSE if !$self->{start_in_progress} && !$self->{race_running}; $self->{semaphore}++; if ($self->{semaphore} <= 5) { $self->{gui}->show_semaphore($self->{semaphore}); my $timeout = $SEMAPHORE_STEP; $timeout += $SEMAPHORE_STEP * rand() if $self->{semaphore} == 5; Glib::Timeout->add($timeout, \&semaphore_step, $self); } elsif ($self->{semaphore} == 6) { $self->{race_running} = 1; $self->{race_running_since} = $self->{now}; $self->{start_in_progress} = undef; $self->{gui}->show_semaphore(0); Glib::Timeout->add($SEMAPHORE_STEP, \&semaphore_step, $self); } else { $self->{gui}->show_semaphore(undef); $self->{semaphore} = undef; $self->{sound}->start(); } return FALSE; } sub race_end { my ($self) = @_; $self->{race_running} = 0; } sub race_setup { my ($self, $rounds, $time) = @_; if ($time - $self->{qualification_setup} < 1) { $self->{round} = 0; } else { if ($rounds) { $self->{race_rounds} = $rounds; } else { $self->{race_rounds} = 0; } } $self->{round} = 0; $self->print_rounds; $self->{best_lap} = undef; $self->{gui}->show_semaphore(undef); $self->{race_running} = 0; $self->{qualification_running} = 0; $self->{start_in_progress} = 0; $self->{race_finishing} = 0; $self->{gui}->time(undef); $self->{gui}->best_lap(undef); for my $car (0..5) { $self->car($car)->reset; } } sub print_rounds { my ($self) = @_; my $msg; if ($self->{qualification_running} || $self->{now} - $self->{qualification_setup} < 1) { $msg = 'Qualification: ' . $self->{race_rounds} . ($self->{race_rounds} == 1 ? ' round' : ' rounds'); } elsif ($self->{race_rounds}) { $msg = $self->{round} . '/' . $self->{race_rounds}; } else { $msg = $self->{round}; } $self->{gui}->rounds($msg); } sub notify_best_lap { my ($self, $id, $time, $who) = @_; return if !defined $time || $time == 0; # print "Check_best_lap $time $who vs ", # defined $self->{best_lap} ? $self->{best_lap} : 'undef', # "\n"; if (!defined $self->{best_lap} || $time < $self->{best_lap}) { $self->{best_lap} = $time; $self->{gui}->best_lap($time, $who); for my $car (0..5) { $self->car($car)->set_global_best($car == $id); $self->car($car)->print_best_lap; } if (!$self->{race_running} || $self->{round} > 1) { # skip the first round in the race $self->{sound}->best_lap($id); } return 1; } return 0; } sub qualification_setup { my ($self, $rounds, $cars, $time) = @_; return if $self->{qualification_running}; for my $car (0..5) { $self->car($car)->set_lap(undef); $self->car($car)->set_laptime(undef); } $self->{qualification_setup} = $time; $self->{race_rounds} = $rounds; $self->{qualification_cars} = $cars; $self->{gui}->time(undef); $self->{gui}->best_lap(undef); $self->print_rounds; } sub packet_received { my ($self, $time) = @_; $self->{now} = $time; if ($self->{race_running}) { $self->{gui}->time($time - $self->{race_running_since}); } } sub recalc_order { my ($self, $now) = @_; return if !$self->{race_running}; my @laps; my @times; for my $id (0..5) { $laps[$id] = $self->car($id)->{lap} // -1; $times[$id] = $self->car($id)->{first_finish_time} // $now; } my @new_order = sort { $laps[$b] <=> $laps[$a] || $times[$a] <=> $times[$b] || $a <=> $b; } (0..5); my $lap_max = $laps[$new_order[0]]; my $lap_max_changed = 0; if (defined $lap_max && defined $self->{round} && $lap_max != $self->{round}) { $self->{round} = $lap_max; $lap_max_changed = 1; $self->print_rounds; } if ($self->{round} && $self->{race_rounds} && $self->{round} > $self->{race_rounds}) { if (!$self->{race_finishing}) { $self->{sound}->winner($new_order[0]); } $self->{race_finishing} = 1; } for my $id (0..5) { my $car = $new_order[$id]; if ($self->car($car)->{order} != $id) { $self->car($car)->set_order($id); } } return ($lap_max_changed, $lap_max, $times[$new_order[0]]); } sub recalc_qual_order { my ($self) = @_; return if !$self->{qualification_running}; my @times; for my $id (0..5) { $times[$id] = $self->car($id)->{best_lap}; if (!defined $times[$id] || $times[$id] <= 0) { $times[$id] = 999_999; } } my @new_order = sort { $times[$a] <=> $times[$b] || $a <=> $b; } (0..5); my $best_time = $times[$new_order[0]]; for my $id (0..5) { my $car = $new_order[$id]; if ($self->car($car)->{order} != $id) { $self->car($car)->set_order($id); } } return ($times[$new_order[0]]); } sub finish_line { my ($self, $time, $regular, @cars) = @_; my %processed; my $was_processed; for my $car (@cars) { if ($self->car($car)->finish_line($time, $regular)) { $processed{$car} = 1; $was_processed = 1; } } return if !$was_processed; if ($self->{qualification_running}) { my ($best) = $self->recalc_qual_order; for my $car (0..5) { $self->car($car)->recalc_qual_distance($best); } return; } my ($lap_max_changed, $lap_max, $time_min) = $self->recalc_order($time); for my $car (0..5) { if ($processed{$car}) { $self->car($car)->recalc_distance( $lap_max, $time_min, $self->{race_finishing}, ); } elsif ($lap_max_changed) { $self->car($car)->greyout_distance; } } } 1;