123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- using BsWidget.BeatSaberHttpStatus;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Drawing.Text;
- using System.IO;
- using System.Linq;
- using System.Windows.Forms;
- namespace BsWidget
- {
- internal class MainForm : BaseForm
- {
- private Timer _updateTimer;
- private BlockingCollection<UpdateFlags> _queue;
- private BeatSaberHttpStatusClient _client;
- [Flags]
- private enum UpdateFlags
- {
- BeatMap = 1 << 0,
- Performance = 1 << 1,
- NoteFullyCut = 1 << 2,
- ClearAll = 1 << 3,
- }
- public Image SongIcon { get; set; }
- public string SongName { get; set; }
- public string SongSubName { get; set; }
- public string SongArtist { get; set; }
- public string BeatMapper { get; set; }
- public string Difficulty { get; set; }
- public double SongBpm { get; set; }
- public double SongNjs { get; set; }
- public int CurrentScore { get; set; }
- public int CurrentCombo { get; set; }
- public string CurrentRank { get; set; }
- public int CurrentMaxScore { get; set; }
- /// <summary> FinalScore,CurrentCombo </summary>
- public List<Tuple<int, int>> CutHistory { get; set; }
- public MainForm()
- {
- KeyPreview = true;
- _client = new BeatSaberHttpStatusClient();
- _client.Event += BeatSaber_Event;
- Text = "Beat Saber Status Widget";
- _queue = new BlockingCollection<UpdateFlags>();
- CutHistory = new List<Tuple<int, int>>();
- _updateTimer = new Timer { Interval = 25 };
- _updateTimer.Tick += UpdateTimer_Tick;
- }
- protected override void OnLoad(EventArgs e)
- {
- base.OnLoad(e);
- Font = new Font("", 20, FontStyle.Bold, GraphicsUnit.Pixel);
- TopMost = true;
- WindowState = FormWindowState.Normal;
- Left = 0;
- Top = 0;
- ClientSize = new Size(1920 / 2, 1080 / 2);
- _updateTimer.Start();
- _client.Start();
- }
- protected override void OnClosing(CancelEventArgs e)
- {
- _client.Stop();
- _updateTimer.Stop();
- base.OnClosing(e);
- }
- protected override void OnKeyDown(KeyEventArgs e)
- {
- base.OnKeyDown(e);
- if (e.KeyCode == Keys.Escape) Application.Exit();
- }
- private void BeatSaber_Event(object sender, BeatSaberStatusEventArgs e)
- {
- if (e.Event == "noteMissed")
- {
- var bp = 0;
- }
- var flags = (UpdateFlags)0;
- if (e.Event == "menu" && CutHistory.Count > 0)
- {
- CutHistory.Clear();
- flags |= UpdateFlags.ClearAll;
- }
- if (null != e.Status.Beatmap)
- {
- var bytes = Convert.FromBase64String(e.Status.Beatmap.SongCover);
- using var stream = new MemoryStream(bytes);
- var newImg = Image.FromStream(stream);
- var old = SongIcon;
- SongIcon = newImg;
- old?.Dispose();
- SongName = e.Status.Beatmap.SongName;
- SongSubName = e.Status.Beatmap.SongSubName;
- SongArtist = e.Status.Beatmap.SongAuthorName;
- BeatMapper = e.Status.Beatmap.LevelAuthorName;
- if (string.IsNullOrEmpty(BeatMapper)) BeatMapper = "Unknown Mapper";
- Difficulty = e.Status.Beatmap.Difficulty;
- if (Difficulty == "ExpertPlus") Difficulty = "Expert+";
- SongBpm = e.Status.Beatmap.SongBPM;
- SongNjs = e.Status.Beatmap.NoteJumpSpeed;
- flags |= UpdateFlags.BeatMap;
- }
- if (null != e.Status.Performance)
- {
- CurrentScore = e.Status.Performance.Score;
- CurrentCombo = e.Status.Performance.Combo;
- CurrentRank = e.Status.Performance.Rank;
- CurrentMaxScore = e.Status.Performance.CurrentMaxScore;
- flags |= UpdateFlags.Performance;
- }
- if (e.Event == "noteFullyCut" && null != e.NoteCut?.FinalScore)
- {
- CutHistory.Add(new Tuple<int, int>(e.NoteCut.FinalScore.Value, CurrentCombo));
- flags |= UpdateFlags.NoteFullyCut;
- }
- if (flags == 0)
- {
- var bp = 0;
- }
- else
- {
- _queue.Add(flags);
- }
- }
- private void UpdateTimer_Tick(object sender, EventArgs e)
- {
- if (_queue.Count == 0) return;
- UpdateGraphic();
- }
- protected override void RenderGraphic(Graphics g)
- {
- if (_queue.Count == 0) return;
- var flags = _queue.Take();
- if (flags.HasFlag(UpdateFlags.ClearAll)) g.Clear(Color.Transparent);
- g.SetHighQuality();
- if (flags.HasFlag(UpdateFlags.BeatMap))
- {
- // Cover: 10,10 128x128
- // SongName: 148,10
- // SongSubName: 148,40
- // SongArtist: 148,70
- // Mapper: 148,100
- // Diff/BMP/NJS: 10,148
- //clear region 0,0 Wx180
- g.ClearRect(Color.Transparent, 0, 0, ViewSize.Width, 180);
- //draw cover and text
- g.DrawImage(SongIcon, 10, 10, 128, 128);
- g.DrawRectangle(Pens.White, 10, 10, 128, 128);
- g.DrawString(SongName, Font, Brushes.White, 148, 10);
- g.DrawString(SongSubName, Font, Brushes.White, 148, 40);
- g.DrawString(SongArtist, Font, Brushes.White, 148, 70);
- g.DrawString(BeatMapper, Font, Brushes.White, 148, 100);
- g.DrawString($"[{Difficulty}] {SongBpm} BPM {SongNjs} NJS", Font, Brushes.White, 10, 148);
- }
- if (flags.HasFlag(UpdateFlags.Performance))
- {
- //clear region 0,180 Wx180
- g.ClearRect(Color.Transparent, 0, 180, ViewSize.Width, 25);
- //draw text
- g.DrawString($"{CurrentScore:N0} POINTS {CurrentCombo} COMBO {CurrentRank} RANK", Font, Brushes.White, 10, 180);
- }
- if (flags.HasFlag(UpdateFlags.NoteFullyCut) && CutHistory.Count > 1)
- {
- const int ChLeft = 10;
- const int ChTop = 210;
- const int ChWidth = 320;
- const int ChHeight = 100;
- const int ChBottom = ChTop + ChHeight;
- const float RecordPixel = 2;
- const float LineWidth = 3f;
- const int DisplayCount = (int)(ChWidth / RecordPixel);
- using var scorePen = new Pen(Color.FromArgb(233, 255, 0, 0), LineWidth) { LineJoin = LineJoin.Round };
- using var comboPen = new Pen(Color.FromArgb(233, 0, 0, 255), LineWidth) { LineJoin = LineJoin.Round };
- //clear region 0,210 Wx200
- g.ClearRect(Color.Transparent, 0, ChTop - 3, ViewSize.Width, ChHeight + 6);
- //draw chart
- using var bgBrush = new SolidBrush(Color.FromArgb(90, 0, 0, 0));
- g.FillRectangle(bgBrush, ChLeft, ChTop, ChWidth, ChHeight);
- g.DrawRectangle(Pens.White, ChLeft - 1, ChTop - 1, ChWidth + 2, ChHeight + 2);
- var skip = CutHistory.Count > DisplayCount ? CutHistory.Count - DisplayCount : 0;
- var items = CutHistory.Skip(skip).ToArray();
- var minScore = items.Select(p => p.Item1).Min();
- var maxScore = items.Select(p => p.Item1).Max();
- var rngScore = maxScore - minScore;
- if (rngScore < 1) rngScore = 1;
- var minCombo = items.Select(p => p.Item2).Min();
- var maxCombo = items.Select(p => p.Item2).Max();
- var rngCombo = maxCombo - minCombo;
- if (rngCombo < 1) rngCombo = 1;
- var x = ChWidth - items.Length * RecordPixel + ChLeft + 1;
- var scorePts = new List<PointF>();
- var comboPts = new List<PointF>();
- for (int i = 0; i < items.Length; i++)
- {
- var item = items[i];
- scorePts.Add(new PointF(x + RecordPixel * i, (ChBottom - 1) - (ChHeight - 2) * ((float)(item.Item1 - minScore) / rngScore)));
- comboPts.Add(new PointF(x + RecordPixel * i, (ChBottom - 1) - (ChHeight - 2) * ((float)(item.Item2 - minCombo) / rngCombo)));
- }
- g.DrawLines(scorePen, scorePts.ToArray());
- g.DrawLines(comboPen, comboPts.ToArray());
- }
- }
- }
- internal static class GdiPlusExt
- {
- public static void ClearRect(this Graphics g, Color color, int x, int y, int w, int h)
- {
- g.SetClip(new Rectangle(x, y, w, h));
- g.Clear(color);
- g.ResetClip();
- }
- public static void SetHighQuality(this Graphics g)
- {
- g.CompositingMode = CompositingMode.SourceOver;
- g.InterpolationMode = InterpolationMode.HighQualityBicubic;
- g.PixelOffsetMode = PixelOffsetMode.HighQuality;
- g.SmoothingMode = SmoothingMode.HighQuality;
- g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
- }
- }
- }
|