i live in spain but the S is silent
All checks were successful
Build and push image / Build (push) Successful in 49s
All checks were successful
Build and push image / Build (push) Successful in 49s
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<UserControl x:Class="ksBroadcastingTestClient.Broadcasting.BroadcastingEventView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ksBroadcastingTestClient.Broadcasting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="50" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="70"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding Evt.Type}" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Evt.Msg}" />
|
||||
<TextBlock Grid.Column="2" Text="{Binding Evt.CarData.RaceNumber}" />
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for BroadcastingEventView.xaml
|
||||
/// </summary>
|
||||
public partial class BroadcastingEventView : UserControl
|
||||
{
|
||||
public BroadcastingEventView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
using ksBroadcastingNetwork.Structs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class BroadcastingEventViewModel : KSObservableObject
|
||||
{
|
||||
public BroadcastingEvent Evt { get; }
|
||||
public KSRelayCommand PlayReplayCommand { get; }
|
||||
|
||||
public BroadcastingEventViewModel(BroadcastingEvent evt, Action<BroadcastingEvent, float, float> onHighlightReplayDelegate)
|
||||
{
|
||||
Evt = evt;
|
||||
PlayReplayCommand = new KSRelayCommand((o) =>
|
||||
{
|
||||
float secondsBack = 5f;
|
||||
float duration = 10f;
|
||||
switch (Evt.Type)
|
||||
{
|
||||
case ksBroadcastingNetwork.BroadcastingCarEventType.GreenFlag:
|
||||
secondsBack = 7f;
|
||||
duration = 25f;
|
||||
break;
|
||||
case ksBroadcastingNetwork.BroadcastingCarEventType.PenaltyCommMsg:
|
||||
secondsBack = 10f;
|
||||
duration = 6f;
|
||||
break;
|
||||
case ksBroadcastingNetwork.BroadcastingCarEventType.Accident:
|
||||
secondsBack = 10f;
|
||||
duration = 10f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
float requestedTime = Evt.TimeMs - (secondsBack * 1000);
|
||||
|
||||
onHighlightReplayDelegate(Evt, requestedTime, duration);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ksBroadcastingNetwork;
|
||||
using ksBroadcastingNetwork.Structs;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class BroadcastingViewModel : KSObservableObject
|
||||
{
|
||||
public ObservableCollection<CarViewModel> Cars { get; } = new ObservableCollection<CarViewModel>();
|
||||
public TrackViewModel TrackVM { get => Get<TrackViewModel>(); private set => Set(value); }
|
||||
public KSRelayCommand RequestFocusedCarCommand { get; }
|
||||
|
||||
private List<BroadcastingNetworkProtocol> _clients = new List<BroadcastingNetworkProtocol>();
|
||||
|
||||
public BroadcastingViewModel()
|
||||
{
|
||||
RequestFocusedCarCommand = new KSRelayCommand(RequestFocusedCar);
|
||||
}
|
||||
|
||||
private void RequestFocusedCar(object obj)
|
||||
{
|
||||
var car = obj as CarViewModel;
|
||||
if (car != null)
|
||||
{
|
||||
foreach (var client in _clients)
|
||||
{
|
||||
// mssing readonly check, will skip this as the ACC client has to handle this as well
|
||||
client.SetFocus(Convert.ToUInt16(car.CarIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RequestHudPageChange(string requestedHudPage)
|
||||
{
|
||||
foreach (var client in _clients)
|
||||
{
|
||||
// mssing readonly check, will skip this as the ACC client has to handle this as well
|
||||
client.RequestHUDPage(requestedHudPage);
|
||||
}
|
||||
}
|
||||
|
||||
private void RequestCameraChange(string camSet, string camera)
|
||||
{
|
||||
foreach (var client in _clients)
|
||||
{
|
||||
// mssing readonly check, will skip this as the ACC client has to handle this as well
|
||||
client.SetCamera(camSet, camera);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RegisterNewClient(ACCUdpRemoteClient newClient)
|
||||
{
|
||||
if (newClient.MsRealtimeUpdateInterval > 0)
|
||||
{
|
||||
// This client will send realtime updates, we should listen
|
||||
newClient.MessageHandler.OnTrackDataUpdate += MessageHandler_OnTrackDataUpdate;
|
||||
newClient.MessageHandler.OnEntrylistUpdate += MessageHandler_OnEntrylistUpdate;
|
||||
newClient.MessageHandler.OnRealtimeUpdate += MessageHandler_OnRealtimeUpdate;
|
||||
newClient.MessageHandler.OnRealtimeCarUpdate += MessageHandler_OnRealtimeCarUpdate;
|
||||
}
|
||||
|
||||
_clients.Add(newClient.MessageHandler);
|
||||
}
|
||||
|
||||
private void MessageHandler_OnTrackDataUpdate(string sender, TrackData trackUpdate)
|
||||
{
|
||||
if (TrackVM?.TrackId != trackUpdate.TrackId)
|
||||
{
|
||||
if (TrackVM != null)
|
||||
{
|
||||
TrackVM.OnRequestCameraChange -= RequestCameraChange;
|
||||
TrackVM.OnRequestHudPageChange -= RequestHudPageChange;
|
||||
}
|
||||
|
||||
|
||||
TrackVM = new TrackViewModel(trackUpdate.TrackId, trackUpdate.TrackName, trackUpdate.TrackMeters);
|
||||
TrackVM.OnRequestCameraChange += RequestCameraChange;
|
||||
TrackVM.OnRequestHudPageChange += RequestHudPageChange;
|
||||
}
|
||||
|
||||
// The track cams may update in between
|
||||
TrackVM.Update(trackUpdate);
|
||||
}
|
||||
|
||||
private void MessageHandler_OnEntrylistUpdate(string sender, CarInfo carUpdate)
|
||||
{
|
||||
CarViewModel vm = Cars.SingleOrDefault(x => x.CarIndex == carUpdate.CarIndex);
|
||||
if (vm == null)
|
||||
{
|
||||
vm = new CarViewModel(carUpdate.CarIndex);
|
||||
Cars.Add(vm);
|
||||
}
|
||||
|
||||
vm.Update(carUpdate);
|
||||
}
|
||||
|
||||
private void MessageHandler_OnRealtimeUpdate(string sender, RealtimeUpdate update)
|
||||
{
|
||||
if (TrackVM != null)
|
||||
TrackVM.Update(update);
|
||||
|
||||
foreach (var carVM in Cars)
|
||||
{
|
||||
carVM.SetFocused(update.FocusedCarIndex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (TrackVM?.TrackMeters > 0)
|
||||
{
|
||||
var sortedCars = Cars.OrderBy(x => x.SplinePosition).ToArray();
|
||||
for (int i = 1; i < sortedCars.Length; i++)
|
||||
{
|
||||
var carAhead = sortedCars[i - 1];
|
||||
var carBehind = sortedCars[i];
|
||||
var splineDistance = Math.Abs(carAhead.SplinePosition - carBehind.SplinePosition);
|
||||
while (splineDistance > 1f)
|
||||
splineDistance -= 1f;
|
||||
|
||||
carBehind.GapFrontMeters = splineDistance * TrackVM.TrackMeters;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void MessageHandler_OnRealtimeCarUpdate(string sender, RealtimeCarUpdate carUpdate)
|
||||
{
|
||||
var vm = Cars.FirstOrDefault(x => x.CarIndex == carUpdate.CarIndex);
|
||||
if (vm == null)
|
||||
{
|
||||
// Oh, we don't have this car yet. In this implementation, the Network protocol will take care of this
|
||||
// so hopefully we will display this car in the next cycles
|
||||
return;
|
||||
}
|
||||
|
||||
vm.Update(carUpdate);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class CameraSetViewModel : KSObservableObject
|
||||
{
|
||||
public string CameraSetName { get; }
|
||||
public ObservableCollection<CameraViewModel> Cameras { get; } = new ObservableCollection<CameraViewModel>();
|
||||
|
||||
public Brush CamsetForeground { get => Get<Brush>(); private set => Set(value); }
|
||||
public Brush CamsetBackground { get => Get<Brush>(); private set => Set(value); }
|
||||
|
||||
public CameraSetViewModel(string camSetName)
|
||||
{
|
||||
CameraSetName = camSetName;
|
||||
SetActive("nope", "nope");
|
||||
}
|
||||
|
||||
public void SetActive(string camSet, string camera)
|
||||
{
|
||||
var isActive = CameraSetName == camSet;
|
||||
if(isActive)
|
||||
{
|
||||
CamsetForeground = Brushes.Yellow;
|
||||
CamsetBackground = Brushes.Black;
|
||||
}
|
||||
else
|
||||
{
|
||||
CamsetForeground = Brushes.Black;
|
||||
CamsetBackground = null;
|
||||
}
|
||||
|
||||
foreach (var item in Cameras)
|
||||
item.SetActive(isActive, camera);
|
||||
}
|
||||
|
||||
internal void Update(List<string> cameraNames)
|
||||
{
|
||||
foreach (var cameraUpdateName in cameraNames)
|
||||
{
|
||||
var camVM = Cameras.SingleOrDefault(x => x.CameraName == cameraUpdateName);
|
||||
if (camVM == null)
|
||||
{
|
||||
camVM = new CameraViewModel(CameraSetName, cameraUpdateName);
|
||||
Cameras.Add(camVM);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we check if we have to remove one of our cameras
|
||||
var toRemove = new List<CameraViewModel>();
|
||||
foreach (var camVM in Cameras)
|
||||
{
|
||||
if (!cameraNames.Contains(camVM.CameraName))
|
||||
toRemove.Add(camVM);
|
||||
}
|
||||
|
||||
foreach (var item in toRemove)
|
||||
Cameras.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class CameraViewModel : KSObservableObject
|
||||
{
|
||||
public string CameraSetName { get; }
|
||||
public string CameraName { get; }
|
||||
public Brush CameraForeground { get => Get<Brush>(); private set => Set(value); }
|
||||
public Brush CameraBackground { get => Get<Brush>(); private set => Set(value); }
|
||||
|
||||
|
||||
public CameraViewModel(string cameraSetName, string cameraName)
|
||||
{
|
||||
CameraSetName = cameraSetName;
|
||||
CameraName = cameraName;
|
||||
SetActive(false, "nope");
|
||||
}
|
||||
|
||||
internal void SetActive(bool camSetActive, string activeCamera)
|
||||
{
|
||||
if (camSetActive && CameraName == activeCamera)
|
||||
{
|
||||
CameraForeground = Brushes.Black;
|
||||
CameraBackground = Brushes.Yellow;
|
||||
}
|
||||
else
|
||||
{
|
||||
CameraForeground = Brushes.Black;
|
||||
CameraBackground = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using ksBroadcastingNetwork;
|
||||
using ksBroadcastingNetwork.Structs;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class CarViewModel : KSObservableObject
|
||||
{
|
||||
public int CarIndex { get; }
|
||||
public int RaceNumber { get => Get<int>(); private set => Set(value); }
|
||||
public int CarModelEnum { get => Get<int>(); private set => Set(value); }
|
||||
public string TeamName { get => Get<string>(); private set => Set(value); }
|
||||
public int CupCategoryEnum { get => Get<int>(); private set => Set(value); }
|
||||
public DriverViewModel CurrentDriver { get => Get<DriverViewModel>(); private set => Set(value); }
|
||||
|
||||
public IEnumerable<DriverViewModel> InactiveDrivers { get { return Drivers.Where(x => x.DriverIndex != CurrentDriver?.DriverIndex); } }
|
||||
public ObservableCollection<DriverViewModel> Drivers { get; } = new ObservableCollection<DriverViewModel>();
|
||||
|
||||
public CarLocationEnum CarLocation { get => Get<CarLocationEnum>(); private set => Set(value); }
|
||||
public int Delta { get => Get<int>(); private set => Set(value); }
|
||||
public string DeltaString { get => Get<string>(); private set => Set(value); }
|
||||
public Brush DeltaColor { get => Get<Brush>(); private set => Set(value); }
|
||||
public int Gear { get => Get<int>(); private set => Set(value); }
|
||||
public int Kmh { get => Get<int>(); private set => Set(value); }
|
||||
public int Position { get => Get<int>(); private set => Set(value); }
|
||||
public int CupPosition { get => Get<int>(); private set => Set(value); }
|
||||
public int TrackPosition { get => Get<int>(); private set => Set(value); }
|
||||
public float SplinePosition { get => Get<float>(); private set => Set(value); }
|
||||
public float WorldX { get => Get<float>(); private set => Set(value); }
|
||||
public float WorldY { get => Get<float>(); private set => Set(value); }
|
||||
public float Yaw { get => Get<float>(); private set => Set(value); }
|
||||
public int Laps { get => Get<int>(); private set => Set(value); }
|
||||
public LapViewModel BestLap { get => Get<LapViewModel>(); private set => Set(value); }
|
||||
public LapViewModel LastLap { get => Get<LapViewModel>(); private set => Set(value); }
|
||||
public LapViewModel CurrentLap { get => Get<LapViewModel>(); private set => Set(value); }
|
||||
public string LocationHint { get => Get<string>(); private set => Set(value); }
|
||||
|
||||
public Brush RowForeground { get => Get<Brush>(); private set => Set(value); }
|
||||
public Brush RowBackground { get => Get<Brush>(); private set => Set(value); }
|
||||
public float GapFrontMeters { get => Get<float>(); set
|
||||
{
|
||||
if(Set(value))
|
||||
{
|
||||
NotifyUpdate(nameof(GapText));
|
||||
NotifyUpdate(nameof(GapColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GapText {
|
||||
get {
|
||||
if (Kmh < 10)
|
||||
return "Gap: ---";
|
||||
return $"Gap: {GapFrontMeters / Kmh * 3.6:F1}s ⇅";
|
||||
}
|
||||
}
|
||||
public Brush GapColor {
|
||||
get {
|
||||
if (Kmh < 10)
|
||||
return Brushes.Gray;
|
||||
var seconds = GapFrontMeters / Kmh * 3.6;
|
||||
if (seconds < 0.5)
|
||||
return Brushes.Red;
|
||||
if (seconds < 2.0)
|
||||
return Brushes.DarkOrange;
|
||||
|
||||
return Brushes.Black;
|
||||
}
|
||||
}
|
||||
|
||||
public CarViewModel(ushort carIndex)
|
||||
{
|
||||
CarIndex = carIndex;
|
||||
}
|
||||
|
||||
internal void Update(CarInfo carUpdate)
|
||||
{
|
||||
RaceNumber = carUpdate.RaceNumber;
|
||||
CarModelEnum = carUpdate.CarModelType;
|
||||
TeamName = carUpdate.TeamName;
|
||||
CupCategoryEnum = carUpdate.CupCategory;
|
||||
|
||||
if(carUpdate.Drivers.Count != Drivers.Count)
|
||||
{
|
||||
Drivers.Clear();
|
||||
int driverIndex = 0;
|
||||
foreach(DriverInfo driver in carUpdate.Drivers)
|
||||
{
|
||||
Drivers.Add(new DriverViewModel(driver, driverIndex++));
|
||||
}
|
||||
NotifyUpdate(nameof(InactiveDrivers));
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update(RealtimeCarUpdate carUpdate)
|
||||
{
|
||||
if (carUpdate.CarIndex != CarIndex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Wrong {nameof(RealtimeCarUpdate)}.CarIndex {carUpdate.CarIndex} for {nameof(CarViewModel)}.CarIndex {CarIndex}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurrentDriver?.DriverIndex != carUpdate.DriverIndex)
|
||||
{
|
||||
// The driver has changed!
|
||||
CurrentDriver = Drivers.SingleOrDefault(x => x.DriverIndex == carUpdate.DriverIndex);
|
||||
NotifyUpdate(nameof(InactiveDrivers));
|
||||
}
|
||||
|
||||
CarLocation = carUpdate.CarLocation;
|
||||
Delta = carUpdate.Delta;
|
||||
DeltaString = $"{TimeSpan.FromMilliseconds(Delta):ss\\.f}";
|
||||
if (Delta < -100)
|
||||
DeltaColor = Brushes.Green;
|
||||
else if (Delta > 100)
|
||||
DeltaColor = Brushes.Red;
|
||||
else
|
||||
DeltaColor = null;
|
||||
|
||||
Gear = carUpdate.Gear;
|
||||
Kmh = carUpdate.Kmh;
|
||||
Position = carUpdate.Position;
|
||||
CupPosition = carUpdate.CupPosition;
|
||||
TrackPosition = carUpdate.TrackPosition;
|
||||
SplinePosition = carUpdate.SplinePosition;
|
||||
WorldX = carUpdate.WorldPosX;
|
||||
WorldY = carUpdate.WorldPosY;
|
||||
Yaw = carUpdate.Yaw;
|
||||
Laps = carUpdate.Laps;
|
||||
|
||||
if(BestLap == null && carUpdate.BestSessionLap != null)
|
||||
BestLap = new LapViewModel();
|
||||
if (carUpdate.BestSessionLap != null)
|
||||
BestLap.Update(carUpdate.BestSessionLap);
|
||||
|
||||
if (LastLap == null && carUpdate.LastLap != null)
|
||||
LastLap = new LapViewModel();
|
||||
if (carUpdate.LastLap != null)
|
||||
LastLap.Update(carUpdate.LastLap);
|
||||
|
||||
if (CurrentLap == null && carUpdate.CurrentLap != null)
|
||||
CurrentLap = new LapViewModel();
|
||||
if (carUpdate.CurrentLap != null)
|
||||
CurrentLap.Update(carUpdate.CurrentLap);
|
||||
|
||||
// The location hint will combine stuff like pits, in/outlap
|
||||
if (CarLocation == CarLocationEnum.PitEntry)
|
||||
LocationHint = "IN";
|
||||
else if (CarLocation == CarLocationEnum.Pitlane)
|
||||
LocationHint = "PIT";
|
||||
else if (CarLocation == CarLocationEnum.PitExit)
|
||||
LocationHint = "OUT";
|
||||
else
|
||||
LocationHint = CurrentLap?.LapHint;
|
||||
}
|
||||
|
||||
internal void SetFocused(int focusedCarIndex)
|
||||
{
|
||||
if (CarIndex == focusedCarIndex)
|
||||
{
|
||||
RowForeground = Brushes.Yellow;
|
||||
RowBackground = Brushes.Black;
|
||||
}
|
||||
else
|
||||
{
|
||||
RowForeground = Brushes.Black;
|
||||
RowBackground = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
<UserControl x:Class="ksBroadcastingTestClient.Broadcasting.EntryListView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
|
||||
xmlns:local="clr-namespace:ksBroadcastingTestClient.Broadcasting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Name="EntryListControl">
|
||||
<UserControl.Resources>
|
||||
<CollectionViewSource x:Key="src" Source="{Binding Cars}">
|
||||
<CollectionViewSource.SortDescriptions>
|
||||
<scm:SortDescription PropertyName="Position" Direction="Ascending" />
|
||||
</CollectionViewSource.SortDescriptions>
|
||||
</CollectionViewSource>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<!-- Header -->
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Width="30" Text="P" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Session Position/Rank" />
|
||||
<TextBlock Width="60" Text="Car" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Car number" />
|
||||
<TextBlock Width="60" Text="Drv" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Driver short name" />
|
||||
<TextBlock Width="150" Text="Team/Driver" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Team + Driver name" />
|
||||
<TextBlock Width="50" Text="Laps" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Laps completed" />
|
||||
<TextBlock Width="50" Text="Loc" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Location hints: PEn = PitEntry, PLa = PitLane, PEx = PitExit, OUT = Outlap, IN = Inlap" />
|
||||
<TextBlock Width="40" Text="D" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Delta to best lap" />
|
||||
<TextBlock Width="75" Text="Current" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Current laptime, splits, in/outlap hint" />
|
||||
<TextBlock Width="75" Text="Last" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Driver's last laptime, splits, in/outlap hint" />
|
||||
<TextBlock Width="75" Text="Best" Style="{StaticResource RowSizedText}" TextAlignment="Center" ToolTip="Car's best laptime, splits, in/outlap hint" />
|
||||
</StackPanel>
|
||||
|
||||
<ItemsControl Name="listViewCars" ItemsSource="{Binding Source={StaticResource src}}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:LiveTimingCarView Margin="0,3,0,0" >
|
||||
<local:LiveTimingCarView.InputBindings>
|
||||
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding ElementName=EntryListControl, Path=DataContext.RequestFocusedCarCommand}" CommandParameter="{Binding}" />
|
||||
</local:LiveTimingCarView.InputBindings>
|
||||
</local:LiveTimingCarView>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for EntryListView.xaml
|
||||
/// </summary>
|
||||
public partial class EntryListView : UserControl
|
||||
{
|
||||
public EntryListView()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
AutorefreshSorting();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Just a lazy hack to have the EntryList always sorted by position
|
||||
/// </summary>
|
||||
private async void AutorefreshSorting()
|
||||
{
|
||||
var sorter = FindResource("src") as CollectionViewSource;
|
||||
|
||||
while (sorter != null)
|
||||
{
|
||||
sorter?.View?.Refresh();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class HudPageViewModel : KSObservableObject
|
||||
{
|
||||
public string Caption { get; }
|
||||
public Brush HudPageForeground { get => Get<Brush>(); private set => Set(value); }
|
||||
public Brush HudPageBackground { get => Get<Brush>(); private set => Set(value); }
|
||||
|
||||
public HudPageViewModel(string hudPageCaption)
|
||||
{
|
||||
Caption = hudPageCaption;
|
||||
}
|
||||
|
||||
public void SetActive(string activeHudPage)
|
||||
{
|
||||
if(Caption == activeHudPage)
|
||||
{
|
||||
HudPageForeground = Brushes.Black;
|
||||
HudPageBackground = Brushes.Yellow;
|
||||
}
|
||||
else
|
||||
{
|
||||
HudPageForeground = Brushes.Black;
|
||||
HudPageBackground = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using ksBroadcastingNetwork;
|
||||
using ksBroadcastingNetwork.Structs;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class LapViewModel : KSObservableObject
|
||||
{
|
||||
public int? LaptimeMS { get => Get<int?>(); private set => Set(value); }
|
||||
public string LaptimeString { get => Get<string>(); private set => Set(value); }
|
||||
public int? Split1MS { get => Get<int?>(); private set => Set(value); }
|
||||
public string Split1String { get => Get<string>(); private set => Set(value); }
|
||||
public int? Split2MS { get => Get<int?>(); private set => Set(value); }
|
||||
public string Split2String { get => Get<string>(); private set => Set(value); }
|
||||
public int? Split3MS { get => Get<int?>(); private set => Set(value); }
|
||||
public string Split3String { get => Get<string>(); private set => Set(value); }
|
||||
|
||||
public LapType Type { get => Get<LapType>(); private set => Set(value); }
|
||||
public bool IsValid { get => Get<bool>(); private set => Set(value); }
|
||||
public Brush LaptimeColor { get => Get<Brush>(); private set => Set(value); }
|
||||
public string LapHint { get => Get<string>(); private set => Set(value); }
|
||||
|
||||
internal void Update(LapInfo lapUpdate)
|
||||
{
|
||||
var isChanged = LaptimeMS != lapUpdate.LaptimeMS;
|
||||
if (isChanged)
|
||||
{
|
||||
LaptimeMS = lapUpdate.LaptimeMS;
|
||||
if (LaptimeMS == null)
|
||||
LaptimeString = "--";
|
||||
else
|
||||
LaptimeString = $"{TimeSpan.FromMilliseconds(LaptimeMS.Value):mm\\:ss\\.fff}";
|
||||
|
||||
Split1MS = lapUpdate.Splits.FirstOrDefault();
|
||||
if (Split1MS != null)
|
||||
Split1String = $"{TimeSpan.FromMilliseconds(Split1MS.Value):ss\\.f}";
|
||||
else
|
||||
Split1String = "";
|
||||
|
||||
Split2MS = lapUpdate.Splits.Skip(1).FirstOrDefault();
|
||||
if (Split2MS != null)
|
||||
Split2String = $"{TimeSpan.FromMilliseconds(Split2MS.Value):ss\\.f}";
|
||||
else
|
||||
Split2String = "";
|
||||
|
||||
Split3MS = lapUpdate.Splits.Skip(2).FirstOrDefault();
|
||||
if (Split3MS != null)
|
||||
Split3String = $"{TimeSpan.FromMilliseconds(Split3MS.Value):ss\\.f}";
|
||||
else
|
||||
Split3String = "";
|
||||
|
||||
Type = lapUpdate.Type;
|
||||
IsValid = lapUpdate.IsValidForBest;
|
||||
|
||||
if (!IsValid)
|
||||
LaptimeColor = Brushes.Red;
|
||||
else
|
||||
LaptimeColor = null; // will use default
|
||||
|
||||
if (Type == LapType.Outlap)
|
||||
LapHint = "OUT";
|
||||
else if (Type == LapType.Inlap)
|
||||
LapHint = "IN";
|
||||
else
|
||||
LapHint = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
<UserControl x:Class="ksBroadcastingTestClient.Broadcasting.LiveTimingCarView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ksBroadcastingTestClient.Broadcasting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="70" d:DesignWidth="800">
|
||||
|
||||
<!-- Data -->
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Background="{Binding RowBackground}">
|
||||
<TextBlock Width="30" Text="{Binding Position}" Style="{StaticResource RowSizedText}" FontWeight="Bold" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="60" Text="{Binding RaceNumber, StringFormat={}#{0}}" Style="{StaticResource RowSizedText}" TextAlignment="Center" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="60" Text="{Binding CurrentDriver.ShortName}" Style="{StaticResource RowSizedText}" Foreground="{Binding RowForeground}" />
|
||||
<StackPanel Width="150" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding TeamName}" Style="{StaticResource HalfSizeText}" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding CurrentDriver.DisplayName}" Style="{StaticResource HalfSizeText}" FontWeight="Bold" Foreground="{Binding RowForeground}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Width="50" Text="{Binding Laps}" Style="{StaticResource RowSizedText}" TextAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="50" Text="{Binding LocationHint}" TextAlignment="Center" Style="{StaticResource RowSizedText}" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="40" Text="{Binding DeltaString}" Foreground="{Binding DeltaColor}" TextAlignment="Center" Style="{StaticResource RowSizedText}" />
|
||||
|
||||
<StackPanel Width="75" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding CurrentLap.LaptimeString}" Style="{StaticResource 2ThirdSizeText}" HorizontalAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding CurrentLap.Split1String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="0" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding CurrentLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="1" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding CurrentLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="2" Foreground="{Binding RowForeground}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Width="75" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding LastLap.LaptimeString}" Style="{StaticResource 2ThirdSizeText}" HorizontalAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding LastLap.Split1String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="0" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding LastLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="1" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding LastLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="2" Foreground="{Binding RowForeground}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Width="75" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding BestLap.LaptimeString}" Style="{StaticResource 2ThirdSizeText}" HorizontalAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding BestLap.Split1String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="0" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding BestLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="1" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding BestLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="2" Foreground="{Binding RowForeground}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding GapText, StringFormat=N0}" Foreground="{Binding GapColor}" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LiveTimingCarView.xaml
|
||||
/// </summary>
|
||||
public partial class LiveTimingCarView : UserControl
|
||||
{
|
||||
public LiveTimingCarView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
<UserControl x:Class="ksBroadcastingTestClient.Broadcasting.LiveTimingCarViewModel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ksBroadcastingTestClient.Broadcasting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="70" d:DesignWidth="800">
|
||||
|
||||
<!-- Data -->
|
||||
<StackPanel Orientation="Horizontal" Background="{Binding RowBackground}">
|
||||
<TextBlock Width="30" Text="{Binding Position}" Style="{StaticResource RowSizedText}" FontWeight="Bold" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="60" Text="{Binding RaceNumber, StringFormat={}#{0}}" Style="{StaticResource RowSizedText}" TextAlignment="Center" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="60" Text="{Binding CurrentDriver.ShortName}" Style="{StaticResource RowSizedText}" Foreground="{Binding RowForeground}" />
|
||||
<StackPanel Width="150" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding TeamName}" Style="{StaticResource HalfSizeText}" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding CurrentDriver.DisplayName}" Style="{StaticResource HalfSizeText}" FontWeight="Bold" Foreground="{Binding RowForeground}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Width="50" Text="{Binding Laps}" Style="{StaticResource RowSizedText}" TextAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="50" Text="{Binding LocationHint}" TextAlignment="Center" Style="{StaticResource RowSizedText}" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Width="40" Text="{Binding DeltaString}" Foreground="{Binding DeltaColor}" TextAlignment="Center" Style="{StaticResource RowSizedText}" />
|
||||
|
||||
<StackPanel Width="75" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding CurrentLap.LaptimeString}" Style="{StaticResource 2ThirdSizeText}" HorizontalAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding CurrentLap.Split1String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="0" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding CurrentLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="1" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding CurrentLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="2" Foreground="{Binding RowForeground}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Width="75" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding LastLap.LaptimeString}" Style="{StaticResource 2ThirdSizeText}" HorizontalAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding LastLap.Split1String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="0" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding LastLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="1" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding LastLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="2" Foreground="{Binding RowForeground}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Width="75" Orientation="Vertical">
|
||||
<TextBlock Text="{Binding BestLap.LaptimeString}" Style="{StaticResource 2ThirdSizeText}" HorizontalAlignment="Right" Foreground="{Binding RowForeground}" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="{Binding BestLap.Split1String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="0" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding BestLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="1" Foreground="{Binding RowForeground}" />
|
||||
<TextBlock Text="{Binding BestLap.Split2String}" TextAlignment="Right" Style="{StaticResource 1ThirdSizeText}" Grid.Column="2" Foreground="{Binding RowForeground}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for LiveTimingCarViewModel.xaml
|
||||
/// </summary>
|
||||
public partial class LiveTimingCarViewModel : UserControl
|
||||
{
|
||||
public LiveTimingCarViewModel()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
<UserControl x:Class="ksBroadcastingTestClient.Broadcasting.ReplayControlView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ksBroadcastingTestClient.Broadcasting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="400"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="70"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Control -->
|
||||
<GroupBox Header="Instant Replay (10s each)" VerticalAlignment="Top" Grid.ColumnSpan="2">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Content="-10s" Command="{Binding PlayLiveReplay}" CommandParameter="10" Width="100" Height="28" Margin="3"/>
|
||||
<Button Content="-30s" Command="{Binding PlayLiveReplay}" CommandParameter="30" Width="100" Height="28" Margin="3"/>
|
||||
<Button Content="-1m" Command="{Binding PlayLiveReplay}" CommandParameter="60" Width="100" Height="28" Margin="3"/>
|
||||
<TextBlock Text="Custom:" Margin="3" Height="28" />
|
||||
<TextBox Name="txtPlayBeforeSeconds" Text="120" Width="40" TextAlignment="Right" Height="22" Margin="3" />
|
||||
<TextBlock Text="min, duration:" Margin="3" Height="28" />
|
||||
<TextBox Text="120" Width="40" TextAlignment="Right" Height="22" Margin="3" />
|
||||
<TextBlock Text="s" Margin="3" Height="28" />
|
||||
<Button Content="Custom" Command="{Binding PlayLiveReplay}" CommandParameter="{Binding ElementName=txtPlayBeforeSeconds, Path=Text}" Width="100" Height="28" Margin="3"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
<!-- Event List -->
|
||||
<GroupBox Header="Highlights" Grid.Column="1" Grid.Row="1">
|
||||
<ItemsControl ItemsSource="{Binding BroadcastingEvents}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:BroadcastingEventView Margin="0,10,0,0" >
|
||||
<local:BroadcastingEventView.InputBindings>
|
||||
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding Path=PlayReplayCommand}" />
|
||||
</local:BroadcastingEventView.InputBindings>
|
||||
</local:BroadcastingEventView>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ReplayControlView.xaml
|
||||
/// </summary>
|
||||
public partial class ReplayControlView : UserControl
|
||||
{
|
||||
public ReplayControlView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ksBroadcastingNetwork;
|
||||
using ksBroadcastingNetwork.Structs;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class ReplayControlViewModel : KSObservableObject
|
||||
{
|
||||
public KSRelayCommand PlayLiveReplay { get; }
|
||||
public int LiveReplaySecondsBack { get => Get<int>(); private set => Set(value); }
|
||||
public int LiveReplaySecondsPlaytime { get => Get<int>(); private set => Set(value); }
|
||||
public int CurrentSessionTime { get => Get<int>(); private set => Set(value); }
|
||||
|
||||
private List<ACCUdpRemoteClient> _clients = new List<ACCUdpRemoteClient>();
|
||||
public ObservableCollection<BroadcastingEventViewModel> BroadcastingEvents { get; } = new ObservableCollection<BroadcastingEventViewModel>();
|
||||
|
||||
public ReplayControlViewModel()
|
||||
{
|
||||
PlayLiveReplay = new KSRelayCommand(OnLiveReplay);
|
||||
LiveReplaySecondsBack = 120;
|
||||
LiveReplaySecondsPlaytime = 10;
|
||||
}
|
||||
|
||||
private void OnHighlightReplay(BroadcastingEvent evt, float requestedStartTime, float durationSeconds)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var client in _clients)
|
||||
{
|
||||
client.MessageHandler.RequestInstantReplay(requestedStartTime, durationSeconds * 1000.0f, evt.CarId);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLiveReplay(object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
var secondsBack = Convert.ToInt32(obj);
|
||||
var requestedStartTime = CurrentSessionTime - (secondsBack * 1000);
|
||||
|
||||
foreach (var client in _clients)
|
||||
{
|
||||
client.MessageHandler.RequestInstantReplay(requestedStartTime, secondsBack * 1000.0f);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal void RegisterNewClient(ACCUdpRemoteClient newClient)
|
||||
{
|
||||
newClient.MessageHandler.OnRealtimeUpdate += MessageHandler_OnRealtimeUpdate;
|
||||
newClient.MessageHandler.OnBroadcastingEvent += MessageHandler_OnBroadcastingEvent;
|
||||
_clients.Add(newClient);
|
||||
}
|
||||
|
||||
private void MessageHandler_OnRealtimeUpdate(string sender, ksBroadcastingNetwork.Structs.RealtimeUpdate update)
|
||||
{
|
||||
CurrentSessionTime = Convert.ToInt32(update.SessionTime.TotalMilliseconds);
|
||||
}
|
||||
|
||||
private void MessageHandler_OnBroadcastingEvent(string sender, BroadcastingEvent evt)
|
||||
{
|
||||
if (evt.Type == BroadcastingCarEventType.LapCompleted)
|
||||
// this is a bit spammy
|
||||
return;
|
||||
|
||||
BroadcastingEvents.Insert(0, new BroadcastingEventViewModel(evt, OnHighlightReplay));
|
||||
|
||||
while(BroadcastingEvents.Count > 30)
|
||||
{
|
||||
BroadcastingEvents.Remove(BroadcastingEvents.Last());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
<UserControl x:Class="ksBroadcastingTestClient.Broadcasting.SessionInfoView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ksBroadcastingTestClient.Broadcasting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="160" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<TextBlock Text="{Binding Phase}" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
|
||||
<TextBlock Text="{Binding SessionType}" HorizontalAlignment="Left" Margin="160,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
|
||||
<TextBlock Text="{Binding RemainingTime}" FontWeight="Bold" HorizontalAlignment="Left" Margin="284,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
|
||||
|
||||
<TextBlock Text="SessionTime" HorizontalAlignment="Left" Margin="214,31,0,0" TextAlignment="Right" TextWrapping="Wrap" VerticalAlignment="Top"/>
|
||||
<TextBlock Text="{Binding SessionTimeDisplay}" HorizontalAlignment="Left" Margin="284,31,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
|
||||
|
||||
<TextBlock Text="{Binding TimeOfDayInfo}" FontSize="17" HorizontalAlignment="Left" Margin="10,43,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
|
||||
<TextBlock Text="{Binding TrackDisplayName}" FontSize="18" HorizontalAlignment="Left" Margin="10,61,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
|
||||
<TextBlock Text="{Binding TempInfo}" TextAlignment="Right" HorizontalAlignment="Right" Margin="0,56,0,0" VerticalAlignment="Top"/>
|
||||
<TextBlock Text="{Binding RainInfo}" TextAlignment="Right" HorizontalAlignment="Right" Margin="0,72,0,0" VerticalAlignment="Top"/>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for SessionInfoView.xaml
|
||||
/// </summary>
|
||||
public partial class SessionInfoView : UserControl
|
||||
{
|
||||
public SessionInfoView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ksBroadcastingNetwork;
|
||||
using ksBroadcastingNetwork.Structs;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class SessionInfoViewModel : KSObservableObject
|
||||
{
|
||||
public TimeSpan SessionTime { get => Get<TimeSpan>(); private set => Set(value); }
|
||||
public string SessionTimeDisplay { get => Get<string>(); private set => Set(value); }
|
||||
public string RemainingTime { get => Get<string>(); private set => Set(value); }
|
||||
public string TrackDisplayName { get => Get<string>(); private set => Set(value); }
|
||||
public SessionPhase Phase { get => Get<SessionPhase>(); private set => Set(value); }
|
||||
public RaceSessionType SessionType { get => Get<RaceSessionType>(); private set => Set(value); }
|
||||
|
||||
public string TimeOfDayInfo { get => Get<string>(); private set => Set(value); }
|
||||
public string TempInfo { get => Get<string>(); private set => Set(value); }
|
||||
public string RainInfo { get => Get<string>(); private set => Set(value); }
|
||||
public string CloudInfo { get => Get<string>(); private set => Set(value); }
|
||||
|
||||
internal void RegisterNewClient(ACCUdpRemoteClient newClient)
|
||||
{
|
||||
if (newClient.MsRealtimeUpdateInterval > 0)
|
||||
{
|
||||
// This client will send realtime updates, we should listen
|
||||
newClient.MessageHandler.OnTrackDataUpdate += MessageHandler_OnTrackDataUpdate;
|
||||
newClient.MessageHandler.OnRealtimeUpdate += MessageHandler_OnRealtimeUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
private void MessageHandler_OnRealtimeUpdate(string sender, RealtimeUpdate update)
|
||||
{
|
||||
SessionTime = update.SessionTime;
|
||||
SessionTimeDisplay = SessionTime.ToString("hh\\:mm\\:ss");
|
||||
if(update.SessionEndTime.TotalMilliseconds < 0)
|
||||
{
|
||||
RemainingTime = "∞";
|
||||
}
|
||||
else
|
||||
{
|
||||
var remaining = update.SessionEndTime - update.SessionTime;
|
||||
|
||||
if (update.SessionTime.TotalMilliseconds < 0)
|
||||
RemainingTime = "00:00";
|
||||
else if (remaining.TotalHours > 2)
|
||||
RemainingTime = $"{remaining.TotalHours:F0}h";
|
||||
else if (remaining.TotalMinutes > 60)
|
||||
RemainingTime = $"{remaining.TotalMinutes:F0}m";
|
||||
else
|
||||
RemainingTime = $"{remaining.TotalMinutes:2,F0}:{remaining.Seconds,2}";
|
||||
}
|
||||
|
||||
Phase = update.Phase;
|
||||
SessionType = update.SessionType;
|
||||
|
||||
TimeOfDayInfo = update.TimeOfDay.ToString("hh\\:mm");
|
||||
|
||||
if (update.Clouds < 0.2)
|
||||
CloudInfo = "Sunny";
|
||||
else if (update.Clouds < 0.40)
|
||||
CloudInfo = "Light clouds";
|
||||
else if(update.Clouds < 0.65)
|
||||
CloudInfo = "Cloudy";
|
||||
else
|
||||
CloudInfo = "World's end";
|
||||
|
||||
TempInfo = $"{CloudInfo} {update.AmbientTemp} °C - Track {update.TrackTemp} °C";
|
||||
|
||||
|
||||
if (update.RainLevel < 0.1 && update.Wetness < 0.1)
|
||||
RainInfo = $"Clear";
|
||||
else
|
||||
RainInfo = $"{update.RainLevel:P0} rain, {update.Wetness:P0} wet";
|
||||
|
||||
}
|
||||
|
||||
private void MessageHandler_OnTrackDataUpdate(string sender, TrackData trackUpdate)
|
||||
{
|
||||
TrackDisplayName = trackUpdate.TrackName;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
<UserControl x:Class="ksBroadcastingTestClient.Broadcasting.TrackCameraView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:ksBroadcastingTestClient.Broadcasting"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Name="TrackCamControl">
|
||||
<Grid>
|
||||
<ItemsControl ItemsSource="{Binding CameraSets}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<!-- Each TV set has a head (=set) followed by a list of their cameras -->
|
||||
<StackPanel Orientation="Vertical" >
|
||||
<!-- HEAD -->
|
||||
<Border BorderThickness="1" BorderBrush="Black" Background="{Binding CamsetBackground}">
|
||||
<TextBlock Text="{Binding CameraSetName}" Foreground="{Binding CamsetForeground}" Width="70" TextAlignment="Center" FontWeight="Bold" Margin="5,2,5,2" />
|
||||
</Border>
|
||||
|
||||
<!-- ITEMS = CAMS -->
|
||||
<ItemsControl ItemsSource="{Binding Cameras}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderThickness="1" BorderBrush="Black" Background="{Binding CameraBackground}">
|
||||
<Border.InputBindings>
|
||||
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding ElementName=TrackCamControl, Path=DataContext.RequestCameraSwitchCommand}" CommandParameter="{Binding}" />
|
||||
</Border.InputBindings>
|
||||
<TextBlock Text="{Binding CameraName}" Foreground="{Binding CameraForeground}" Width="90" TextAlignment="Center" Margin="5,2,5,2" />
|
||||
</Border>
|
||||
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- 2nd part: HUD pages; we will put them below the drivable and pit cams, there will be always more space -->
|
||||
<!-- Each TV set has a head (=set) followed by a list of their cameras -->
|
||||
<StackPanel Orientation="Vertical" Width="150" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5, 182, 5, 2">
|
||||
<!-- HEAD -->
|
||||
<Border BorderThickness="1" BorderBrush="Black" Background="Black">
|
||||
<TextBlock Text="HudPages" Foreground="Yellow" TextAlignment="Center" FontWeight="Bold" Margin="5,2,5,2" />
|
||||
</Border>
|
||||
|
||||
<!-- ITEMS = CAMS -->
|
||||
<ItemsControl ItemsSource="{Binding HudPages}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderThickness="1" BorderBrush="Black" Background="{Binding HudPageBackground}">
|
||||
<Border.InputBindings>
|
||||
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding ElementName=TrackCamControl, Path=DataContext.RequestHudSwitchCommand}" CommandParameter="{Binding}" />
|
||||
</Border.InputBindings>
|
||||
<TextBlock Text="{Binding Caption}" Foreground="{Binding HudPageForeground}" Width="90" TextAlignment="Center" Margin="5,2,5,2" />
|
||||
</Border>
|
||||
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for TrackCameraView.xaml
|
||||
/// </summary>
|
||||
public partial class TrackCameraView : UserControl
|
||||
{
|
||||
public TrackCameraView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ksBroadcastingNetwork.Structs;
|
||||
|
||||
namespace ksBroadcastingTestClient.Broadcasting
|
||||
{
|
||||
public class TrackViewModel : KSObservableObject
|
||||
{
|
||||
public int TrackId { get; }
|
||||
public float TrackMeters { get; }
|
||||
public string TrackName { get; }
|
||||
|
||||
public ObservableCollection<CameraSetViewModel> CameraSets { get; } = new ObservableCollection<CameraSetViewModel>();
|
||||
public KSRelayCommand RequestCameraSwitchCommand { get; }
|
||||
public KSRelayCommand RequestHudSwitchCommand { get;}
|
||||
|
||||
public ObservableCollection<HudPageViewModel> HudPages { get; } = new ObservableCollection<HudPageViewModel>();
|
||||
|
||||
public delegate void RequestCameraChangeDelegate(string camSet, string camera);
|
||||
public event RequestCameraChangeDelegate OnRequestCameraChange;
|
||||
|
||||
public delegate void RequestHudChangeDelegate(string requestedHudPage);
|
||||
public event RequestHudChangeDelegate OnRequestHudPageChange;
|
||||
|
||||
public TrackViewModel(int trackId, string trackName, float trackMeters)
|
||||
{
|
||||
TrackId = trackId;
|
||||
TrackName = trackName;
|
||||
TrackMeters = trackMeters;
|
||||
|
||||
RequestCameraSwitchCommand = new KSRelayCommand(RequestCameraSwitch);
|
||||
RequestHudSwitchCommand = new KSRelayCommand(RequestHudPageSwitch);
|
||||
}
|
||||
|
||||
private void RequestHudPageSwitch(object obj)
|
||||
{
|
||||
var hudPageVM = obj as HudPageViewModel;
|
||||
if (hudPageVM != null)
|
||||
OnRequestHudPageChange?.Invoke(hudPageVM.Caption);
|
||||
}
|
||||
|
||||
private void RequestCameraSwitch(object obj)
|
||||
{
|
||||
var cameraVM = obj as CameraViewModel;
|
||||
if(cameraVM != null)
|
||||
OnRequestCameraChange?.Invoke(cameraVM.CameraSetName, cameraVM.CameraName);
|
||||
}
|
||||
|
||||
internal void Update(TrackData trackUpdate)
|
||||
{
|
||||
foreach (var camSetUpdate in trackUpdate.CameraSets)
|
||||
{
|
||||
var camSetVM = CameraSets.SingleOrDefault(x => x.CameraSetName == camSetUpdate.Key);
|
||||
if(camSetVM == null)
|
||||
{
|
||||
camSetVM = new CameraSetViewModel(camSetUpdate.Key);
|
||||
CameraSets.Add(camSetVM);
|
||||
}
|
||||
|
||||
camSetVM.Update(camSetUpdate.Value);
|
||||
}
|
||||
|
||||
// Now we check if we have to remove one of our cam sets
|
||||
var toRemove = new List<CameraSetViewModel>();
|
||||
foreach (var camSetVM in CameraSets)
|
||||
{
|
||||
if (!trackUpdate.CameraSets.ContainsKey(camSetVM.CameraSetName))
|
||||
toRemove.Add(camSetVM);
|
||||
}
|
||||
|
||||
foreach (var item in toRemove)
|
||||
CameraSets.Remove(item);
|
||||
|
||||
|
||||
// The same (but 1d) for the HUD pages
|
||||
foreach (var hudPageCaption in trackUpdate.HUDPages)
|
||||
{
|
||||
var hudPageVM = HudPages.SingleOrDefault(x => x.Caption == hudPageCaption);
|
||||
if(hudPageVM == null)
|
||||
{
|
||||
hudPageVM = new HudPageViewModel(hudPageCaption);
|
||||
HudPages.Add(hudPageVM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update(RealtimeUpdate update)
|
||||
{
|
||||
foreach (var camSet in CameraSets)
|
||||
camSet.SetActive(update.ActiveCameraSet, update.ActiveCamera);
|
||||
|
||||
foreach (var hudPage in HudPages)
|
||||
{
|
||||
hudPage.SetActive(update.CurrentHudPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user