diff --git a/RadarTimingsTester.sln b/RadarTimingsTester.sln new file mode 100644 index 0000000..cd53d91 --- /dev/null +++ b/RadarTimingsTester.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29926.136 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RadarTimingsTester", "RadarTimingsTester\RadarTimingsTester.csproj", "{D733A756-5346-4B34-BA3D-3E004F93C1DC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D733A756-5346-4B34-BA3D-3E004F93C1DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D733A756-5346-4B34-BA3D-3E004F93C1DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D733A756-5346-4B34-BA3D-3E004F93C1DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D733A756-5346-4B34-BA3D-3E004F93C1DC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F0BC5F9F-4B98-4C74-AA3A-4A51786841E3} + EndGlobalSection +EndGlobal diff --git a/RadarTimingsTester/App.config b/RadarTimingsTester/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/RadarTimingsTester/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/RadarTimingsTester/App.xaml b/RadarTimingsTester/App.xaml new file mode 100644 index 0000000..f9ff086 --- /dev/null +++ b/RadarTimingsTester/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/RadarTimingsTester/App.xaml.cs b/RadarTimingsTester/App.xaml.cs new file mode 100644 index 0000000..72576c3 --- /dev/null +++ b/RadarTimingsTester/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace RadarTimingsTester +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/RadarTimingsTester/MainWindow.xaml b/RadarTimingsTester/MainWindow.xaml new file mode 100644 index 0000000..318e1f2 --- /dev/null +++ b/RadarTimingsTester/MainWindow.xaml @@ -0,0 +1,18 @@ + + + + + Drawing Mode + + + + Error + + diff --git a/RadarTimingsTester/MainWindow.xaml.cs b/RadarTimingsTester/MainWindow.xaml.cs new file mode 100644 index 0000000..e6a250a --- /dev/null +++ b/RadarTimingsTester/MainWindow.xaml.cs @@ -0,0 +1,402 @@ +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +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 RadarTimingsTester +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + private enum DrawingMode { Freehand, Waypoint } + private const double RUN_SPEED_WITH_KNIFE = 250; + private class PathData + { + public SolidColorBrush color; + public List lines = new List(); + private double time = 0; + public double Time { get { return time; } } + + public void CalculateTime() + { + float scale = mapScale; + + //Getting this, since a normal radar is 1024 x 1024, but this one is only 600 x 600 + float mod = 1024f / 600f; + + double totalDistance = 0; + for (int i = 0; i < lines.Count; i++) + { + //Adjusting everything to world position, or close enough to where its mostly accurate + double x1 = (lines[i].X1 * mod * scale) + mapPosX; + double x2 = (lines[i].X2 * mod * scale) + mapPosX; + double y1 = (lines[i].Y1 * mod * scale) + mapPosY; + double y2 = (lines[i].Y2 * mod * scale) + mapPosY; + totalDistance += Distance(x1, y1, x2, y2); + } + + //Adjust to get timings when running with a knife, maybe I'll add more weapons in the future. + time = totalDistance / RUN_SPEED_WITH_KNIFE; + } + + public override string ToString() + { + return " Time: " + time.ToString("0.0000s"); + } + } + + private bool ready = false; + private Point currentPoint = new Point(); + private SolidColorBrush currentColor = new SolidColorBrush(); + private BitmapSource generalImage; + private DrawingMode drawingMode = DrawingMode.Freehand; + private int selectedIndex = -1; + private object selectedObj = null; + + private List paths = new List(); + + //Map Data + private static float mapPosX = 0; + private static float mapPosY = 0; + private static float mapScale = 0; + + public MainWindow() + { + InitializeComponent(); + + //Not currently using, but may in the future. + DrawOptions.Items.Add("Freehand"); + DrawOptions.Items.Add("Waypoint"); + DrawOptions.SelectedIndex = 0; + } + + private void LoadMapData(string path) + { + if (File.Exists(path)) + { + string[] txtData = File.ReadAllLines(path); + + foreach (string line in txtData) + { + if (line.Contains("pos_x")) + { + string val = line.Split('"')[3]; + mapPosX = (int)System.Convert.ToSingle(val); + } + if (line.Contains("pos_y")) + { + string val = line.Split('"')[3]; + mapPosY = (int)System.Convert.ToSingle(val); + } + if (line.Contains("scale")) + { + string val = line.Split('"')[3]; + mapScale = System.Convert.ToSingle(val); + } + } + } + } + + private void MainCanvas_MouseDown(object sender, MouseButtonEventArgs e) + { + if (e.ButtonState == MouseButtonState.Pressed) + { + currentPoint = Mouse.GetPosition(MainCanvas); + + bool bad = true; + int safety = 0; + + while(bad) + { + // Define parameters used to create the BitmapSource. + PixelFormat pf = PixelFormats.Bgr32; + int width = 1; + int height = 1; + int rawStride = (width * pf.BitsPerPixel + 7) / 8; + byte[] rawImage = new byte[rawStride * height]; + + // Initialize the image with data. + Random value = new Random(); + value.NextBytes(rawImage); + + // Create a BitmapSource. + generalImage = BitmapSource.Create(width, height, + 10, 10, pf, null, + rawImage, rawStride); + + //Format is Bgr, reversed of normal, so I enter it backwards + currentColor = new SolidColorBrush(Color.FromRgb(rawImage[2], rawImage[1], rawImage[0])); + + bad = false; + + //Check to make sure we get a unique-ish color + int index = GetIndexFromColor(currentColor); + if(index == -1) + { + break; + } + + safety++; + if(safety > 1000) + { + break; + } + } + + if (!ready) + { + paths.Add(new PathData() + { + color = currentColor, + lines = new List() + }); + } + ready = true; + } + } + + private void MainCanvas_MouseMove(object sender, MouseEventArgs e) + { + if (e.LeftButton == MouseButtonState.Pressed && ready) + { + Line line = new Line(); + + line.Stroke = currentColor; + line.StrokeThickness = 3; + line.X1 = currentPoint.X; + line.Y1 = currentPoint.Y; + line.X2 = Mouse.GetPosition(MainCanvas).X; + line.Y2 = Mouse.GetPosition(MainCanvas).Y; + + currentPoint = Mouse.GetPosition(MainCanvas); + + paths[paths.Count - 1].lines.Add(new Line() + { + Stroke = currentColor, + StrokeThickness = 3, + X1 = line.X1, + X2 = line.X2, + Y1 = line.Y1, + Y2 = line.Y2 + }); + + MainCanvas.Children.Add(line); + } + } + + private void MainCanvas_MouseUp(object sender, MouseButtonEventArgs e) + { + ready = false; + + paths[paths.Count - 1].CalculateTime(); + + StackPanel panel = new StackPanel() + { + Orientation = Orientation.Horizontal + }; + + Image myImage = new Image(); + myImage.Width = 10; + myImage.Source = generalImage; + + panel.Children.Add(myImage); + panel.Children.Add(new TextBlock() { Text = paths[paths.Count - 1].ToString() }); + ListOfLines.Items.Add(panel); + } + + private int GetIndexFromColor(SolidColorBrush color) + { + int range = 10; + for (int i = 0; i < paths.Count; i++) + { + bool weGood = false; + for (int j = 0; j < range*range + 1; j++) + { + if(paths[i].color.Color.R == color.Color.R + (j - range)) + { + weGood = true; + break; + } + } + if(!weGood) + { + continue; + } + for (int j = 0; j < range * range + 1; j++) + { + if (paths[i].color.Color.G == color.Color.G + (j - range)) + { + weGood = true; + break; + } + } + if (!weGood) + { + continue; + } + for (int j = 0; j < range * range + 1; j++) + { + if (paths[i].color.Color.B == color.Color.B + (j - range)) + { + weGood = true; + break; + } + } + if (!weGood) + { + continue; + } + return i; + } + return -1; + } + + private int GetIndexFromTime(double time) + { + for (int i = 0; i < paths.Count; i++) + { + if(Math.Abs(paths[i].Time - time) < 0.0001f) + { + return i; + } + } + + return -1; + } + + private void ListOfLines_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + var item = (sender as ListView).SelectedItem; + if (item != null) + { + selectedObj = item; + + TextBlock block = (item as StackPanel).Children[1] as TextBlock; + + //Do a bunch of bullshit to get the time. Hoping time is unique enough of an identifier. If I get complaints/issues with this, I could probably try another method. + int index = GetIndexFromTime(System.Convert.ToDouble(block.Text.Replace("s", string.Empty).Replace("Time:", string.Empty).Trim())); + selectedIndex = index; + + if(selectedIndex != -1) + { + MainCanvas.Children.Clear(); + + //Outline selected path + for (int i = 0; i < paths[selectedIndex].lines.Count; i++) + { + Line outline = new Line(); + outline.X1 = paths[selectedIndex].lines[i].X1; + outline.X2 = paths[selectedIndex].lines[i].X2; + outline.Y1 = paths[selectedIndex].lines[i].Y1; + outline.Y2 = paths[selectedIndex].lines[i].Y2; + outline.Stroke = new SolidColorBrush(Color.FromRgb(255, 255, 255)); + outline.StrokeThickness = 6; + + MainCanvas.Children.Add(outline); + } + } + + RedrawLines(false); + } + } + + private void Remove_Click(object sender, RoutedEventArgs e) + { + if (selectedIndex != -1) + { + ListOfLines.Items.Remove(selectedObj); + paths.RemoveAt(selectedIndex); + + MainCanvas.Children.Clear(); + + RedrawLines(); + } + } + + private void RedrawLines(bool clear = true) + { + if(clear) + { + MainCanvas.Children.Clear(); + } + + for (int i = 0; i < paths.Count; i++) + { + for (int j = 0; j < paths[i].lines.Count; j++) + { + MainCanvas.Children.Add(paths[i].lines[j]); + } + } + } + + private void LoadMapButton_Click(object sender, RoutedEventArgs e) + { + OpenFileDialog fileDialog = new OpenFileDialog(); + fileDialog.Filter = "png files (*.png)|*.png|All files (*.*)|*.*"; + fileDialog.RestoreDirectory = true; + if(fileDialog.ShowDialog() == true) + { + paths.Clear(); + MainCanvas.Children.Clear(); + ListOfLines.Items.Clear(); + + string path = fileDialog.FileName; + ImageBrush imageBrush = new ImageBrush(); + imageBrush.ImageSource = new BitmapImage(new Uri(path)); + + MainCanvas.Background = imageBrush; + + string txtPath = path.Replace("_radar.png", ".txt"); + int layerN = 1; + while(txtPath.Contains("_layer")) + { + txtPath = txtPath.Replace("_layer" + layerN, string.Empty); + layerN++; + + if(layerN > 50) + { + break; //Infinite loop safety check + } + } + + //In case someone adds an invalid image it wont be able to find the .txt + //Might provide a manual method of passing the .txt in the future + if(File.Exists(txtPath) && txtPath.Contains(".txt")) + { + LoadMapData(txtPath); + ErrorText.Visibility = Visibility.Hidden; + } + else + { + ErrorText.Visibility = Visibility.Visible; + ErrorText.Text = "Could not find .txt file associated with radar. Timings will not be accurate. Please report this to 7ark."; + } + } + } + + + //Static utility functions + public static double Distance(double x1, double y1, double x2, double y2) + { + double xPoints = Math.Pow((x2 - x1), 2.0); + double yPoints = Math.Pow((y2 - y1), 2.0); + + return Math.Sqrt(xPoints + yPoints); + } + } +} diff --git a/RadarTimingsTester/Properties/AssemblyInfo.cs b/RadarTimingsTester/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cf5c31a --- /dev/null +++ b/RadarTimingsTester/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RadarTimingsTester")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RadarTimingsTester")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RadarTimingsTester/Properties/Resources.Designer.cs b/RadarTimingsTester/Properties/Resources.Designer.cs new file mode 100644 index 0000000..b636bab --- /dev/null +++ b/RadarTimingsTester/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RadarTimingsTester.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RadarTimingsTester.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/RadarTimingsTester/Properties/Resources.resx b/RadarTimingsTester/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/RadarTimingsTester/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/RadarTimingsTester/Properties/Settings.Designer.cs b/RadarTimingsTester/Properties/Settings.Designer.cs new file mode 100644 index 0000000..e473332 --- /dev/null +++ b/RadarTimingsTester/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace RadarTimingsTester.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/RadarTimingsTester/Properties/Settings.settings b/RadarTimingsTester/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/RadarTimingsTester/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/RadarTimingsTester/RadarTimingsTester.csproj b/RadarTimingsTester/RadarTimingsTester.csproj new file mode 100644 index 0000000..6fdbe8b --- /dev/null +++ b/RadarTimingsTester/RadarTimingsTester.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {D733A756-5346-4B34-BA3D-3E004F93C1DC} + WinExe + RadarTimingsTester + RadarTimingsTester + v4.7.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + \ No newline at end of file