Skip to content

Commit

Permalink
Merge pull request #241 from stanriders/simulate-from-scoreid
Browse files Browse the repository at this point in the history
Add an option to populate score parameters on `SimulateScreen` using score ID
  • Loading branch information
smoogipoo authored Dec 25, 2024
2 parents ddde137 + f6c0a0a commit e647c5f
Showing 1 changed file with 157 additions and 21 deletions.
178 changes: 157 additions & 21 deletions PerformanceCalculatorGUI/Screens/SimulateScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Humanizer;
using osu.Framework;
using osu.Framework.Allocation;
Expand All @@ -23,6 +24,7 @@
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
Expand Down Expand Up @@ -59,6 +61,9 @@ public partial class SimulateScreen : PerformanceCalculatorScreen
private LimitedLabelledNumberBox comboTextBox;
private LimitedLabelledNumberBox scoreTextBox;

private LabelledNumberBox scoreIdTextBox;
private StatefulButton scoreIdPopulateButton;

private GridContainer accuracyContainer;
private LimitedLabelledFractionalNumberBox accuracyTextBox;
private LimitedLabelledNumberBox goodsTextBox;
Expand Down Expand Up @@ -99,12 +104,18 @@ public partial class SimulateScreen : PerformanceCalculatorScreen
[Resolved]
private Bindable<RulesetInfo> ruleset { get; set; }

[Resolved]
private RulesetStore rulesets { get; set; }

[Resolved]
private LargeTextureStore textures { get; set; }

[Resolved]
private SettingsManager configManager { get; set; }

[Resolved]
private APIManager apiManager { get; set; }

[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);

Expand Down Expand Up @@ -208,6 +219,38 @@ private void load(OsuColour osuColour)
Height = 20,
Text = "Score params"
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
scoreIdTextBox = new LabelledNumberBox
{
RelativeSizeAxes = Axes.X,
Width = 0.7f,
Label = "Score ID",
PlaceholderText = "0",
},
scoreIdPopulateButton = new StatefulButton("Populate from score")
{
RelativeSizeAxes = Axes.X,
Width = 0.3f,
Action = () =>
{
if (!string.IsNullOrEmpty(scoreIdTextBox.Current.Value))
{
populateSettingsFromScore(long.Parse(scoreIdTextBox.Current.Value));
}
else
{
notificationDisplay.Display(new Notification("Incorrect score id"));
}
}
}
}
},
accuracyContainer = new GridContainer
{
RelativeSizeAxes = Axes.X,
Expand Down Expand Up @@ -551,24 +594,6 @@ private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
calculateDifficulty();
updateCombo(false);
calculatePerformance();

void updateMissesTextboxes()
{
if (ruleset.Value.ShortName == "osu")
{
// Large tick misses and slider tail misses are only relevant in PP if slider head accuracy exists
if (mods.NewValue.OfType<OsuModClassic>().Any(m => m.NoSliderHeadAccuracy.Value))
{
missesContainer.Content = new[] { new[] { missesTextBox } };
missesContainer.ColumnDimensions = [new Dimension()];
}
else
{
missesContainer.Content = new[] { new[] { missesTextBox, largeTickMissesTextBox, sliderTailMissesTextBox } };
missesContainer.ColumnDimensions = [new Dimension(), new Dimension(), new Dimension()];
}
}
}
}

private void resetBeatmap()
Expand Down Expand Up @@ -690,13 +715,13 @@ private void calculatePerformance()
countMeh = mehsTextBox.Value.Value;
}

var score = RulesetHelper.AdjustManiaScore(scoreTextBox.Value.Value, appliedMods.Value);
int score = RulesetHelper.AdjustManiaScore(scoreTextBox.Value.Value, appliedMods.Value);

try
{
var beatmap = working.GetPlayableBeatmap(ruleset.Value, appliedMods.Value);

var accuracy = accuracyTextBox.Value.Value / 100.0;
double accuracy = accuracyTextBox.Value.Value / 100.0;
Dictionary<HitResult, int> statistics = new Dictionary<HitResult, int>();

if (ruleset.Value.OnlineID != -1)
Expand Down Expand Up @@ -970,7 +995,7 @@ private void showError(Exception e)
{
Logger.Log(e.ToString(), level: LogLevel.Error);

var message = e is AggregateException aggregateException ? aggregateException.Flatten().Message : e.Message;
string message = e is AggregateException aggregateException ? aggregateException.Flatten().Message : e.Message;
showError(message, false);
}

Expand All @@ -981,5 +1006,116 @@ private void showError(string message, bool log = true)

notificationDisplay.Display(new Notification(message));
}

private void populateSettingsFromScore(long scoreId)
{
if (scoreIdPopulateButton.State.Value == ButtonState.Loading)
return;

scoreIdPopulateButton.State.Value = ButtonState.Loading;

Task.Run(async () =>
{
try
{
var scoreInfo = await apiManager.GetJsonFromApi<SoloScoreInfo>($"scores/{scoreId}");

Schedule(() =>
{
if (scoreInfo.BeatmapID != working.BeatmapInfo.OnlineID)
{
beatmapIdTextBox.Text = string.Empty;
changeBeatmap(scoreInfo.BeatmapID.ToString());
}

ruleset.Value = rulesets.GetRuleset(scoreInfo.RulesetID);
appliedMods.Value = scoreInfo.Mods.Select(x => x.ToMod(ruleset.Value.CreateInstance())).ToList();

fullScoreDataSwitch.Current.Value = true;

// TODO: this shouldn't be done in 2 lines
comboTextBox.Value.Value = scoreInfo.MaxCombo;
comboTextBox.Text = scoreInfo.MaxCombo.ToString();

resetMisses();
updateMissesTextboxes();

if (scoreInfo.Statistics.TryGetValue(HitResult.Miss, out int misses))
{
missesTextBox.Value.Value = misses;
missesTextBox.Text = misses.ToString();
}

if (scoreInfo.Statistics.TryGetValue(HitResult.Ok, out int oks))
{
goodsTextBox.Value.Value = oks;
goodsTextBox.Text = oks.ToString();
}

if (scoreInfo.Statistics.TryGetValue(HitResult.Meh, out int mehs))
{
mehsTextBox.Value.Value = mehs;
mehsTextBox.Text = mehs.ToString();
}

if (scoreInfo.Statistics.TryGetValue(HitResult.LargeTickMiss, out int largeTickMisses))
{
largeTickMissesTextBox.Value.Value = largeTickMisses;
largeTickMissesTextBox.Text = largeTickMisses.ToString();
}

if (scoreInfo.Statistics.TryGetValue(HitResult.SliderTailHit, out int sliderTailHits))
{
int sliderTailMisses = scoreInfo.MaximumStatistics[HitResult.SliderTailHit] - sliderTailHits;
sliderTailMissesTextBox.Value.Value = sliderTailMisses;
sliderTailMissesTextBox.Text = sliderTailMisses.ToString();
}

calculateDifficulty();
calculatePerformance();

scoreIdPopulateButton.State.Value = ButtonState.Done;
});
}
catch (Exception e)
{
Schedule(() => showError(e));
}
});
}

private void resetMisses()
{
missesTextBox.Value.Value = 0;
missesTextBox.Text = string.Empty;

largeTickMissesTextBox.Value.Value = 0;
largeTickMissesTextBox.Text = string.Empty;

sliderTailMissesTextBox.Value.Value = 0;
sliderTailMissesTextBox.Text = string.Empty;
}

private void updateMissesTextboxes()
{
if (ruleset.Value.ShortName == "osu")
{
// Large tick misses and slider tail misses are only relevant in PP if slider head accuracy exists
if (appliedMods.Value.OfType<OsuModClassic>().Any(m => m.NoSliderHeadAccuracy.Value))
{
missesContainer.Content = new[] { new[] { missesTextBox } };
missesContainer.ColumnDimensions = [new Dimension()];
}
else
{
missesContainer.Content = new[] { new[] { missesTextBox, largeTickMissesTextBox, sliderTailMissesTextBox } };
missesContainer.ColumnDimensions = [new Dimension(), new Dimension(), new Dimension()];
fixupTextBox(largeTickMissesTextBox);
fixupTextBox(sliderTailMissesTextBox);
}

fixupTextBox(missesTextBox);
}
}
}
}

0 comments on commit e647c5f

Please sign in to comment.