private void timeline_PlaybackPositionChanged(object sender, EventArgs e)
        {
            var text = "";

            BeginPlayerListUpdate();
            minimap.BeginUpdate();

            foreach (var playerInfo in _demo.ReadPlayerInfos(timeline.PlaybackPosition))
            {
                minimap.UpdatePlayer(playerInfo, _demo);
                UpdatePlayerListPlayer(playerInfo);

                if (_selectedPlayers.Contains(playerInfo.EntityID))
                {
                    text += $@"{playerInfo.State.Name} Statistics:

Kills: {playerInfo.Statistics.Kills}
Deaths: {playerInfo.Statistics.Deaths}
K/D Ratio: {(double)playerInfo.Statistics.Kills / (double)playerInfo.Statistics.Deaths:F2}
Assists: {playerInfo.Statistics.Assists}
Score: {playerInfo.Statistics.Score}
MVPs: {playerInfo.Statistics.MVPs}
Ping: {playerInfo.Statistics.Ping}
Clantag: {playerInfo.Statistics.Clantag}
TotalCashSpent: {playerInfo.Statistics.TotalCashSpent}

";
                }
            }

            EndPlayerListUpdate();
            minimap.EndUpdate();

            if (text == "")
            {
                playerInfos.Text = "Click on a player to view more information about them.";
            }
            else
            {
                playerInfos.Text = text;
            }

            killfeed.SetKills(_demo.ReadRecentKills(timeline.PlaybackPosition, 5));
        }
        public HeatmapWindow(CancellationTokenSource cts, DemoData demo, HashSet <int> selectedPlayers, int selectionStart, int selectionEnd, int radius)
        {
            InitializeComponent();

            this.demo            = demo;
            this.selectedPlayers = selectedPlayers;
            this.selectionStart  = selectionStart;
            this.selectionEnd    = selectionEnd;

            minimapImage.Source = Assets.GetMinimap(demo.MapName);

            _cts  = cts;
            _ct   = cts.Token;
            _task = Task.Run(() =>
            {
                var counter = 0;

                using (var heatmap = new Heatmap(1024, 1024))
                    using (var stroke = new HeatmapStamp(radius))
                    {
                        for (int i = selectionStart; i <= selectionEnd; i++)
                        {
                            _ct.ThrowIfCancellationRequested();

                            foreach (var player in demo.ReadPlayerInfos(i))
                            {
                                if (!selectedPlayers.Contains(player.EntityID))
                                {
                                    continue;
                                }

                                if (player.State.Team == DemoInfo.Team.Spectate)
                                {
                                    continue;
                                }

                                if (!player.State.IsAlive)
                                {
                                    continue;
                                }

                                var realPos = demo.WorldSpaceToMinimapSpace(new Vector(player.Position.PositionX, player.Position.PositionY));

                                heatmap.AddPoint((int)realPos.X, (int)realPos.Y, stroke);
                            }

                            if ((++counter & 0xF) == 0)
                            {
                                var percentage = (double)(i - selectionStart) / (selectionEnd - selectionStart);

                                Dispatcher.Invoke(() =>
                                {
                                    progressBar.Value = percentage;
                                });
                            }
                        }

                        Dispatcher.Invoke(() =>
                        {
                            heatmapImage.Source = heatmap.CreateHeatmap();
                        });
                    }
            }, _ct);
        }