Add GamePhase logic

This commit is contained in:
hodasemi 2023-01-18 17:02:20 +01:00
parent 144c7ab5f0
commit a10698962e
7 changed files with 163 additions and 45 deletions

View file

@ -1,12 +1,12 @@
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?> <?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
<root> <root>
<grid id="grid" x_dim="9" y_dim="1"> <grid id="grid" x_dim="11" y_dim="1">
<label id="place" x_slot="0" y_slot="0" text_color="black" text_alignment="right"></label> <label id="place" x_slot="0" y_slot="0" text_color="black" text_alignment="right"></label>
<label <label
id="name" id="name"
x_slot="1" y_slot="0" x_size="6" text_color="black" text_alignment="left"></label> x_slot="1" y_slot="0" x_size="6" text_color="black" text_alignment="left"></label>
<label <label
id="time_behind" id="time"
x_slot="7" y_slot="0" x_size="2" text_color="black" text_alignment="right"></label> x_slot="7" y_slot="0" x_size="4" text_color="black" text_alignment="right"></label>
</grid> </grid>
</root> </root>

View file

@ -1,5 +1,5 @@
<?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?> <?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
<root reference_width="2560" reference_height="1440"> <root reference_width="2560" reference_height="1440">
<grid id="main_grid" x_dim="1" y_dim="25" x_offset="10" y_offset="10" width="350" height="875" <grid id="main_grid" x_dim="1" y_dim="25" x_offset="10" y_offset="10" width="400" height="875"
vert_align="top" hori_align="left" margin="0" padding="0"> </grid> vert_align="top" hori_align="left" margin="0" padding="0"> </grid>
</root> </root>

View file

@ -53,7 +53,14 @@ impl LeaderBoard {
.to_string() .to_string()
} }
fn race_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> { fn update_leaderboard<F>(
&mut self,
vehicle_scorings: &[VehicleScoringInfoV01],
f: F,
) -> Result<()>
where
F: Fn(&mut LeaderBoardEntry, &VehicleScoringInfoV01) -> Result<()>,
{
for vehicle_scoring in vehicle_scorings { for vehicle_scoring in vehicle_scorings {
let driver_name = Self::c_char_to_string(vehicle_scoring.mDriverName); let driver_name = Self::c_char_to_string(vehicle_scoring.mDriverName);
@ -69,8 +76,8 @@ impl LeaderBoard {
} }
entry.update_place(vehicle_scoring.mPlace)?; entry.update_place(vehicle_scoring.mPlace)?;
entry.update_time_behind_leader(vehicle_scoring.mTimeBehindLeader)?;
entry.update_time_behind_next(vehicle_scoring.mTimeBehindNext)?; f(entry, vehicle_scoring)?;
} }
None => { None => {
let entry = LeaderBoardEntry::new( let entry = LeaderBoardEntry::new(
@ -80,6 +87,7 @@ impl LeaderBoard {
vehicle_scoring.mPlace, vehicle_scoring.mPlace,
vehicle_scoring.mTimeBehindLeader, vehicle_scoring.mTimeBehindLeader,
vehicle_scoring.mTimeBehindNext, vehicle_scoring.mTimeBehindNext,
vehicle_scoring.mBestLapTime,
)?; )?;
self.entries.push(entry); self.entries.push(entry);
@ -120,19 +128,39 @@ impl LeaderBoard {
} }
} }
if self.entries.is_empty() {
self.gui.disable()?;
} else {
self.gui.enable()?;
}
Ok(()) Ok(())
} }
fn race_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
self.update_leaderboard(vehicle_scorings, |entry, scoring| {
entry.update_time_behind_leader(scoring.mTimeBehindLeader)
})
}
fn quali_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
self.update_leaderboard(vehicle_scorings, |entry, scoring| {
entry.update_best_lap(scoring.mBestLapTime)
})
}
} }
impl UiOverlay for LeaderBoard {} impl UiOverlay for LeaderBoard {}
impl DataReceiver for LeaderBoard { impl DataReceiver for LeaderBoard {
fn game_phase_change(&mut self, phase: GamePhase) -> Result<()> {
match phase {
GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race => self.gui.enable(),
_ => self.gui.disable(),
}
}
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race => true,
_ => false,
}
}
fn scoring_update( fn scoring_update(
&mut self, &mut self,
phase: GamePhase, phase: GamePhase,
@ -141,13 +169,13 @@ impl DataReceiver for LeaderBoard {
write_log!("=================== leader board: scoring update ==================="); write_log!("=================== leader board: scoring update ===================");
match phase { match phase {
GamePhase::TestDay => self.gui.disable()?, GamePhase::Practice | GamePhase::Qualifying => {
GamePhase::Practice => self.gui.disable()?, self.quali_leaderboard(vehicle_scorings)?
GamePhase::Qualifying => self.gui.disable()?,
GamePhase::Warmup => self.gui.disable()?,
GamePhase::Race => {
self.race_leaderboard(vehicle_scorings)?;
} }
GamePhase::Race => self.race_leaderboard(vehicle_scorings)?,
_ => (),
} }
write_log!("leader board update finished"); write_log!("leader board update finished");
@ -177,13 +205,14 @@ struct LeaderBoardEntry {
place: u8, place: u8,
time_behind_leader: f64, time_behind_leader: f64,
time_behind_next: f64, time_behind_next: f64,
best_lap: f64,
snippet: Arc<GuiSnippet>, snippet: Arc<GuiSnippet>,
grid: Arc<Grid>, grid: Arc<Grid>,
name_label: Arc<Label>, name_label: Arc<Label>,
place_label: Arc<Label>, place_label: Arc<Label>,
time_behind_label: Arc<Label>, time_label: Arc<Label>,
place_updated: bool, place_updated: bool,
} }
@ -196,17 +225,18 @@ impl LeaderBoardEntry {
place: u8, place: u8,
time_behind_leader: f64, time_behind_leader: f64,
time_behind_next: f64, time_behind_next: f64,
best_lap: f64,
) -> Result<Self> { ) -> Result<Self> {
let snippet = GuiSnippet::from_str(gui_handler, LeaderBoard::ENTRY)?; let snippet = GuiSnippet::from_str(gui_handler, LeaderBoard::ENTRY)?;
let background = snippet.element("grid")?; let background = snippet.element("grid")?;
let name_label: Arc<Label> = snippet.element("name")?; let name_label: Arc<Label> = snippet.element("name")?;
let place_label: Arc<Label> = snippet.element("place")?; let place_label: Arc<Label> = snippet.element("place")?;
let time_behind_label: Arc<Label> = snippet.element("time_behind")?; let time_label: Arc<Label> = snippet.element("time")?;
name_label.set_text(&name)?; name_label.set_text(&name)?;
place_label.set_text(place)?; place_label.set_text(place)?;
time_behind_label.set_text(format!("{:.3}", time_behind_leader))?; time_label.set_text("---")?;
Ok(Self { Ok(Self {
id, id,
@ -215,13 +245,14 @@ impl LeaderBoardEntry {
place, place,
time_behind_leader, time_behind_leader,
time_behind_next, time_behind_next,
best_lap,
snippet, snippet,
grid: background, grid: background,
name_label, name_label,
place_label, place_label,
time_behind_label, time_label,
place_updated: true, place_updated: true,
}) })
@ -264,21 +295,49 @@ impl LeaderBoardEntry {
} }
pub fn update_time_behind_leader(&mut self, time: f64) -> Result<()> { pub fn update_time_behind_leader(&mut self, time: f64) -> Result<()> {
self.time_behind_leader = time; if self.time_behind_leader != time {
self.time_behind_leader = time;
// check if we are leader // check if we are leader
if self.time_behind_leader == 0.0 { if self.time_behind_leader == 0.0 {
self.time_behind_label.set_text("---") self.time_label.set_text("---")?;
} else { } else {
self.time_behind_label self.time_label
.set_text(format!("+{:.3}", self.time_behind_leader)) .set_text(format!("+{:.3}", self.time_behind_leader))?;
}
} }
Ok(())
}
pub fn update_best_lap(&mut self, time: f64) -> Result<()> {
if self.best_lap != time {
self.best_lap = time;
if self.best_lap < 0.0 {
self.time_label.set_text("---")?;
} else {
let text = if self.best_lap > 60.0 {
let full_minutes = (self.best_lap / 60.0).floor();
let remainder = self.best_lap - (full_minutes * 60.0);
format!("{:.0}:{:.3}", full_minutes, remainder)
} else {
format!("{:.3}", self.best_lap)
};
self.time_label.set_text(text)?;
}
}
Ok(())
} }
pub fn update_time_behind_next(&mut self, time: f64) -> Result<()> { pub fn update_time_behind_next(&mut self, time: f64) -> Result<()> {
self.time_behind_next = time; self.time_behind_next = time;
Ok(()) self.time_label
.set_text(format!("+{:.3}", self.time_behind_next))
} }
pub fn needs_resorting(&self) -> bool { pub fn needs_resorting(&self) -> bool {

View file

@ -231,6 +231,19 @@ impl Pedals {
impl UiOverlay for Pedals {} impl UiOverlay for Pedals {}
impl DataReceiver for Pedals { impl DataReceiver for Pedals {
fn game_phase_change(&mut self, _phase: GamePhase) -> Result<()> {
Ok(())
}
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race | GamePhase::Warmup => {
true
}
_ => false,
}
}
fn scoring_update( fn scoring_update(
&mut self, &mut self,
_phase: GamePhase, _phase: GamePhase,

View file

@ -268,6 +268,19 @@ impl Radar {
impl UiOverlay for Radar {} impl UiOverlay for Radar {}
impl DataReceiver for Radar { impl DataReceiver for Radar {
fn game_phase_change(&mut self, _phase: GamePhase) -> Result<()> {
Ok(())
}
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race | GamePhase::Warmup => {
true
}
_ => false,
}
}
fn scoring_update( fn scoring_update(
&mut self, &mut self,
_phase: GamePhase, _phase: GamePhase,

View file

@ -26,11 +26,7 @@ impl Watermark {
impl UiOverlay for Watermark {} impl UiOverlay for Watermark {}
impl DataReceiver for Watermark { impl DataReceiver for Watermark {
fn scoring_update( fn game_phase_change(&mut self, phase: GamePhase) -> Result<()> {
&mut self,
phase: GamePhase,
_vehicle_scoring: &[VehicleScoringInfoV01],
) -> Result<()> {
match phase { match phase {
GamePhase::TestDay => self.gui.enable()?, GamePhase::TestDay => self.gui.enable()?,
_ => self.gui.disable()?, _ => self.gui.disable()?,
@ -39,6 +35,20 @@ impl DataReceiver for Watermark {
Ok(()) Ok(())
} }
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
_ => false,
}
}
fn scoring_update(
&mut self,
_phase: GamePhase,
_vehicle_scoring: &[VehicleScoringInfoV01],
) -> Result<()> {
Ok(())
}
fn telemetry_update( fn telemetry_update(
&mut self, &mut self,
_player_id: Option<i32>, _player_id: Option<i32>,

View file

@ -8,6 +8,10 @@ use crate::write_log;
use super::UiOverlay; use super::UiOverlay;
pub trait DataReceiver { pub trait DataReceiver {
fn game_phase_change(&mut self, phase: GamePhase) -> Result<()>;
fn update_for_phase(&self, phase: GamePhase) -> bool;
fn scoring_update( fn scoring_update(
&mut self, &mut self,
phase: GamePhase, phase: GamePhase,
@ -21,13 +25,14 @@ pub trait DataReceiver {
) -> Result<()>; ) -> Result<()>;
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum GamePhase { pub enum GamePhase {
TestDay, TestDay,
Practice, Practice,
Qualifying, Qualifying,
Warmup, Warmup,
Race, Race,
None,
} }
impl TryFrom<i32> for GamePhase { impl TryFrom<i32> for GamePhase {
@ -53,6 +58,7 @@ pub struct RFactorData {
start_time: Instant, start_time: Instant,
player_id: Option<i32>, player_id: Option<i32>,
previous_game_phase: GamePhase,
receivers: Vec<Rc<RefCell<dyn UiOverlay>>>, receivers: Vec<Rc<RefCell<dyn UiOverlay>>>,
} }
@ -69,6 +75,7 @@ impl RFactorData {
start_time, start_time,
player_id: None, player_id: None,
previous_game_phase: GamePhase::None,
receivers: Vec::new(), receivers: Vec::new(),
}) })
@ -107,14 +114,28 @@ impl RFactorData {
} }
} }
let phase = GamePhase::try_from(scoring_info.mSession)?; {
let phase = GamePhase::try_from(scoring_info.mSession)?;
write_log!(format!("GamePhase: {:?}", phase)); if self.previous_game_phase != phase {
self.previous_game_phase = phase;
for receiver in self.receivers.iter() {
receiver
.borrow_mut()
.game_phase_change(self.previous_game_phase)?;
}
}
write_log!(format!("GamePhase: {:?}", self.previous_game_phase));
}
for receiver in self.receivers.iter() { for receiver in self.receivers.iter() {
receiver let mut rec_mut = receiver.borrow_mut();
.borrow_mut()
.scoring_update(phase, &vehicle_scorings)?; if rec_mut.update_for_phase(self.previous_game_phase) {
rec_mut.scoring_update(self.previous_game_phase, &vehicle_scorings)?;
}
} }
} }
@ -124,9 +145,11 @@ impl RFactorData {
write_log!("new telemetry update"); write_log!("new telemetry update");
for receiver in self.receivers.iter() { for receiver in self.receivers.iter() {
receiver let mut rec_mut = receiver.borrow_mut();
.borrow_mut()
.telemetry_update(self.player_id, &telemetries)?; if rec_mut.update_for_phase(self.previous_game_phase) {
rec_mut.telemetry_update(self.player_id, &telemetries)?;
}
} }
} }