Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OsuHealthProcessor that uses the legacy drain rate algorithm #25418

Merged
merged 17 commits into from
Nov 22, 2023

Conversation

smoogipoo
Copy link
Contributor

Intended to resolve #24365

I've tried to get the existing lazer algorithm to match more closely to osu!stable but the best I can do is +/- 40% difference in drain rate. I was told this wasn't acceptable, so here we are.

This PR adds two health processors: LegacyOsuHealthProcessor which implements the legacy algorithm exactly, and OsuHealthProcessor which is lazer's take on it.

Results can be found here: https://docs.google.com/spreadsheets/d/1npZHxhV-ZtQxDQ1KM2OBAXBcwhrfl-8ZWccq7xqQ-g8/edit#gid=2107058059

Drain rate exactly matches osu!stable if the combo end bonus is not added in. This is an osu!stable-specific gameplay mechanic which we have not yet implemented in lazer (geki bonus, for successfully completing a combo). We've briefly discussed this and may consider implementing it in lazer in the future - we had earlier intentions of doing so at least.

There are bugs in the implementation - breaks are not handled correctly by the legacy algorithm, and there are also edge cases around sliders/spinners. These are taken verbatim from osu!stable without having been fixed, but have been noted in the implementation.

There's an osu-tools branch with a new command that can be used to test this. I haven't PR'd it because it's a very specific use-case of testing out this one PR.

I haven't done any work on other rulesets yet.

currentHpUncapped = hp_bar_maximum;

double lowestHp = currentHp;
double lastTime = DrainStartTime;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just gonna note that this isn't exactly the same as stable, since DrainStartTime comes from DrainingHealthProcessor ctor param, which comes from OsuHealthProcessor ctor param, which comes from OsuRuleset.CreateHealthProcessor() ctor param, which is given as playableBeatmap.HitObjects[0].StartTime:

https://github.com/ppy/osu/blob/master/osu.Game/Screens/Play/Player.cs#L230

while stable gives it hitObjectManager.hitObjects[0].StartTime - hitObjectManager.PreEmpt (https://github.com/peppy/osu-stable-reference/blob/master/osu!/GameModes/Play/Rulesets/Osu/RulesetOsu_HpCalculation.cs).

Likely not a huge deal in practice, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smoogipoo can you confirm you're okay with this?

Copy link
Contributor Author

@smoogipoo smoogipoo Nov 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the value is a best effort but is also accurate to how lazer works - health only starts draining from that time.

@bdach
Copy link
Collaborator

bdach commented Nov 14, 2023

Checking with stable, the legacy health processor values look to be matching stable log output on a few random maps from the sheet, so the ground truth appears to be correct there.

@peppy peppy self-requested a review November 16, 2023 06:13
@peppy peppy self-requested a review November 17, 2023 09:28
/// <summary>
/// The drain rate as a proportion of the total health drained per millisecond.
/// </summary>
public double DrainRate { get; private set; } = 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is coming from the old code, but how about we get rid of this weird default value? It would mean the whole health would drain in 1 millisecond, which makes little sense.

diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs
index a8808d08e5..4a651e2650 100644
--- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs
@@ -44,7 +44,7 @@ public partial class DrainingHealthProcessor : HealthProcessor
         /// <summary>
         /// The drain rate as a proportion of the total health drained per millisecond.
         /// </summary>
-        public double DrainRate { get; private set; } = 1;
+        public double DrainRate { get; private set; }
 
         /// <summary>
         /// The beatmap.
@@ -139,8 +139,6 @@ protected override void Reset(bool storeResults)
         {
             base.Reset(storeResults);
 
-            DrainRate = 1;
-
             if (storeResults)
                 DrainRate = ComputeDrainRate();
 


// TODO: This doesn't handle overlapping/sequential breaks correctly (/b/614).
// Subtract any break time from the duration since the last object
if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is shockingly written, but I'm not going to touch it as it does match the stable code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peppy peppy self-requested a review November 21, 2023 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Health drain should match expectations better
3 participants