Skip to content

Commit 2ff9223

Browse files
feat: merge ppy ppy#37740 - Ranked Play damage breakdown and individual multipliers
Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/6aa30dd0-1e55-4f16-92fb-52d719f8c0a5 Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
1 parent 0898f10 commit 2ff9223

9 files changed

Lines changed: 297 additions & 90 deletions

File tree

osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayStageOverlay.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,13 @@ public override void SetUpSteps()
4040
[Test]
4141
public void TestBasic()
4242
{
43-
double multiplier = 1.0;
44-
45-
AddSliderStep<double>("set multiplier", 1, 5, 2, value => multiplier = value);
4643
AddStep("create", () => Child = new RankedPlayStageOverlay("Pick Phase", RankedPlayColourScheme.BLUE)
4744
{
4845
PickingUser = new APIUser
4946
{
5047
Id = 2,
5148
Username = "peppy",
5249
},
53-
Multiplier = multiplier,
5450
});
5551
}
5652

@@ -64,7 +60,6 @@ public void TestLongUsername()
6460
Id = 226597,
6561
Username = "WWWWWWWWWWWWWWWWWWWW",
6662
},
67-
Multiplier = 2,
6863
});
6964
}
7065

@@ -78,7 +73,6 @@ public void TestColourScheme()
7873
Id = 2,
7974
Username = "peppy",
8075
},
81-
Multiplier = 2,
8276
});
8377
AddStep("create red", () => Child = new RankedPlayStageOverlay("Pick Phase", RankedPlayColourScheme.RED)
8478
{
@@ -87,7 +81,6 @@ public void TestColourScheme()
8781
Id = 2,
8882
Username = "peppy",
8983
},
90-
Multiplier = 2,
9184
});
9285
}
9386
}

osu.Game.Tests/Visual/RankedPlay/TestSceneResultsScreen.cs

Lines changed: 111 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ public override void SetUpSteps()
4040
}
4141

4242
[Test]
43-
[Explicit("Test exercises correct stopping of audio playback. Has no assertions, only useful when checked manually by a human.")]
44-
public void TestAllSamplesStopOnExit()
43+
public void TestBasic()
4544
{
4645
AddStep("set results state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.Results, state =>
4746
{
@@ -56,7 +55,7 @@ public void TestAllSamplesStopOnExit()
5655
RawDamage = 123_456,
5756
Damage = 123_456,
5857
OldLife = 500_000,
59-
NewLife = 500_000 - 123_456,
58+
NewLife = 500_000 - 123_456
6059
};
6160

6261
userInfo.Life = 500_000 - 123_456;
@@ -73,48 +72,72 @@ public void TestAllSamplesStopOnExit()
7372
}
7473
}
7574
}).WaitSafely());
76-
AddWaitStep("wait for samples to start playing", 5);
77-
AddRepeatStep("exit", () => screen.Exit(), 2);
7875
}
7976

8077
[Test]
81-
public void TestBasic()
78+
public void TestMultiplier()
8279
{
8380
AddStep("set results state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.Results, state =>
8481
{
8582
int losingPlayer = state.Users.Keys.First();
8683

84+
state.DamageMultiplier = 1.5;
85+
8786
foreach (var (id, userInfo) in state.Users)
8887
{
8988
if (id == losingPlayer)
9089
{
90+
userInfo.DamageMultiplier = 0.5;
9191
userInfo.DamageInfo = new RankedPlayDamageInfo
9292
{
9393
RawDamage = 123_456,
94-
Damage = 123_456,
95-
OldLife = 500_000,
96-
NewLife = 500_000 - 123_456,
94+
Damage = 123_456 * 2,
95+
OldLife = 1_000_000,
96+
NewLife = 1_000_000 - 123_456 * 2,
97+
Multiplier = 2,
98+
DirectDamage = 123_456,
9799
};
98100

99-
userInfo.Life = 500_000 - 123_456;
101+
userInfo.Life = 1_000_000 - 123_456 * 2;
100102
}
101103
else
102104
{
105+
userInfo.DamageMultiplier = 0.5;
103106
userInfo.DamageInfo = new RankedPlayDamageInfo
104107
{
105108
RawDamage = 0,
106109
Damage = 0,
107110
OldLife = 1_000_000,
108111
NewLife = 1_000_000,
112+
Multiplier = 2,
113+
DirectDamage = 0,
109114
};
110115
}
111116
}
112117
}).WaitSafely());
113118
}
114119

115120
[Test]
116-
public void TestMultiplier()
121+
public void TestMissingScores()
117122
{
123+
AddStep("setup request handler", () =>
124+
{
125+
Func<APIRequest, bool>? defaultRequestHandler = ((DummyAPIAccess)API).HandleRequest;
126+
127+
((DummyAPIAccess)API).HandleRequest = request =>
128+
{
129+
switch (request)
130+
{
131+
case IndexPlaylistScoresRequest index:
132+
index.TriggerSuccess(new IndexedMultiplayerScores());
133+
return true;
134+
135+
default:
136+
return defaultRequestHandler?.Invoke(request) ?? false;
137+
}
138+
};
139+
});
140+
118141
AddStep("set results state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.Results, state =>
119142
{
120143
int losingPlayer = state.Users.Keys.First();
@@ -132,8 +155,6 @@ public void TestMultiplier()
132155
OldLife = 1_000_000,
133156
NewLife = 1_000_000 - 123_456 * 2,
134157
};
135-
136-
userInfo.Life = 1_000_000 - 123_456 * 2;
137158
}
138159
else
139160
{
@@ -150,43 +171,104 @@ public void TestMultiplier()
150171
}
151172

152173
[Test]
153-
public void TestMissingScores()
174+
[Explicit("Test exercises correct stopping of audio playback. Has no assertions, only useful when checked manually by a human.")]
175+
public void TestAllSamplesStopOnExit()
154176
{
155-
AddStep("setup request handler", () =>
177+
AddStep("set results state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.Results, state =>
156178
{
157-
Func<APIRequest, bool>? defaultRequestHandler = ((DummyAPIAccess)API).HandleRequest;
179+
int losingPlayer = state.Users.Keys.First();
158180

159-
((DummyAPIAccess)API).HandleRequest = request =>
181+
foreach (var (id, userInfo) in state.Users)
160182
{
161-
switch (request)
183+
if (id == losingPlayer)
162184
{
163-
case IndexPlaylistScoresRequest index:
164-
index.TriggerSuccess(new IndexedMultiplayerScores());
165-
return true;
185+
userInfo.DamageInfo = new RankedPlayDamageInfo
186+
{
187+
RawDamage = 123_456,
188+
Damage = 123_456,
189+
OldLife = 500_000,
190+
NewLife = 500_000 - 123_456,
191+
};
166192

167-
default:
168-
return defaultRequestHandler?.Invoke(request) ?? false;
193+
userInfo.Life = 500_000 - 123_456;
169194
}
170-
};
171-
});
195+
else
196+
{
197+
userInfo.DamageInfo = new RankedPlayDamageInfo
198+
{
199+
RawDamage = 0,
200+
Damage = 0,
201+
OldLife = 1_000_000,
202+
NewLife = 1_000_000,
203+
};
204+
}
205+
}
206+
}).WaitSafely());
207+
AddWaitStep("wait for samples to start playing", 5);
208+
AddRepeatStep("exit", () => screen.Exit(), 2);
209+
}
172210

211+
[Test]
212+
public void TestDamageBreakdown()
213+
{
173214
AddStep("set results state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.Results, state =>
174215
{
175216
int losingPlayer = state.Users.Keys.First();
176217

177-
state.DamageMultiplier = 2;
218+
foreach (var (id, userInfo) in state.Users)
219+
{
220+
if (id == losingPlayer)
221+
{
222+
userInfo.DamageInfo = new RankedPlayDamageInfo
223+
{
224+
RawDamage = 600_000,
225+
Damage = 1_100_000,
226+
OldLife = 1_000_000,
227+
NewLife = 1,
228+
DirectDamage = 500_000,
229+
Multiplier = 2,
230+
BonusDamage = 100_000
231+
};
232+
233+
userInfo.Life = 653_088;
234+
}
235+
else
236+
{
237+
userInfo.DamageInfo = new RankedPlayDamageInfo
238+
{
239+
RawDamage = 0,
240+
Damage = 0,
241+
OldLife = 1_000_000,
242+
NewLife = 1_000_000,
243+
};
244+
}
245+
}
246+
}).WaitSafely());
247+
}
248+
249+
[Test]
250+
public void TestDamageBreakdownWithNegativeValues()
251+
{
252+
AddStep("set results state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.Results, state =>
253+
{
254+
int losingPlayer = state.Users.Keys.First();
178255

179256
foreach (var (id, userInfo) in state.Users)
180257
{
181258
if (id == losingPlayer)
182259
{
183260
userInfo.DamageInfo = new RankedPlayDamageInfo
184261
{
185-
RawDamage = 123_456,
186-
Damage = 123_456 * 2,
262+
RawDamage = 500_000,
263+
Damage = 200_000,
187264
OldLife = 1_000_000,
188-
NewLife = 1_000_000 - 123_456 * 2,
265+
NewLife = 800_000,
266+
DirectDamage = 600_000,
267+
Multiplier = 0.5,
268+
BonusDamage = -100_000
189269
};
270+
271+
userInfo.Life = 653_088;
190272
}
191273
else
192274
{

osu.Game/Online/Multiplayer/MatchTypes/RankedPlay/RankedPlayDamageInfo.cs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,45 +12,68 @@ public class RankedPlayDamageInfo : IEquatable<RankedPlayDamageInfo>
1212
{
1313
/// <summary>
1414
/// Total amount of damage dealt.
15+
/// Calculated as <see cref="DirectDamage"/> * <see cref="Multiplier"/> + <see cref="BonusDamage"/>.
1516
/// </summary>
17+
/// <remarks>
18+
/// Not required since adding additional properties (<see cref="DirectDamage"/> / <see cref="BonusDamage"/> / <see cref="Multiplier"/>).
19+
/// Could potentially be replaced with a property doing the above calculation.
20+
/// </remarks>
1621
[Key(0)]
17-
public required int Damage { get; init; }
22+
public int Damage { get; set; }
1823

1924
/// <summary>
2025
/// Damage dealt before multipliers are applied.
26+
/// Calculated as <see cref="DirectDamage"/> + <see cref="BonusDamage"/>.
2127
/// </summary>
28+
/// <remarks>
29+
/// Not required since adding additional properties (<see cref="DirectDamage"/> / <see cref="BonusDamage"/> / <see cref="Multiplier"/>).
30+
/// Can potentially be removed in the future.
31+
/// </remarks>
2232
[Key(1)]
23-
public required int RawDamage { get; init; }
33+
public int RawDamage { get; set; }
2434

2535
/// <summary>
2636
/// Life before damage was applied.
2737
/// </summary>
2838
[Key(2)]
29-
public required int OldLife { get; init; }
39+
public int OldLife { get; set; }
3040

3141
/// <summary>
3242
/// Life after damage was applied.
3343
/// </summary>
3444
[Key(3)]
35-
public required int NewLife { get; init; }
45+
public int NewLife { get; set; }
3646

37-
public bool Equals(RankedPlayDamageInfo? other)
38-
{
39-
if (other is null) return false;
40-
if (ReferenceEquals(this, other)) return true;
47+
/// <summary>
48+
/// Direct damage dealt based on score difference.
49+
/// </summary>
50+
[Key(4)]
51+
public int DirectDamage { get; set; }
4152

42-
return Damage == other.Damage && RawDamage == other.RawDamage && OldLife == other.OldLife && NewLife == other.NewLife;
43-
}
53+
/// <summary>
54+
/// The multiplier of <see cref="DirectDamage"/>.
55+
/// </summary>
56+
[Key(5)]
57+
public double Multiplier { get; set; } = 1;
58+
59+
/// <summary>
60+
/// Damage dealt for winning a round.
61+
/// </summary>
62+
[Key(6)]
63+
public int BonusDamage { get; set; }
4464

45-
public override bool Equals(object? obj)
65+
public bool Equals(RankedPlayDamageInfo? other)
4666
{
47-
if (obj is null) return false;
48-
if (ReferenceEquals(this, obj)) return true;
49-
if (obj.GetType() != GetType()) return false;
67+
if (other == null)
68+
return false;
5069

51-
return Equals((RankedPlayDamageInfo)obj);
70+
return Damage == other.Damage
71+
&& RawDamage == other.RawDamage
72+
&& OldLife == other.OldLife
73+
&& NewLife == other.NewLife
74+
&& DirectDamage == other.DirectDamage
75+
&& Multiplier == other.Multiplier
76+
&& BonusDamage == other.BonusDamage;
5277
}
53-
54-
public override int GetHashCode() => HashCode.Combine(Damage, RawDamage, OldLife, NewLife);
5578
}
5679
}

osu.Game/Online/Multiplayer/MatchTypes/RankedPlay/RankedPlayRoomState.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class RankedPlayRoomState : MatchRoomState
2727
/// A multiplier applied to life point damage.
2828
/// </summary>
2929
[Key(2)]
30-
public double DamageMultiplier { get; set; } = 1;
30+
public double DamageMultiplier { get; set; } = 0.5;
3131

3232
/// <summary>
3333
/// A dictionary containing all users in the room.

osu.Game/Online/Multiplayer/MatchTypes/RankedPlay/RankedPlayUserInfo.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,11 @@ public class RankedPlayUserInfo
4949
/// </summary>
5050
[Key(5)]
5151
public int RoundsWon { get; set; }
52+
53+
/// <summary>
54+
/// This player's individual damage multiplier.
55+
/// </summary>
56+
[Key(6)]
57+
public double DamageMultiplier { get; set; } = 0.5;
5258
}
5359
}

0 commit comments

Comments
 (0)