diff --git a/app/Models/Solo/Score.php b/app/Models/Solo/Score.php
index 6b4632a30a2..1c4841b188c 100644
--- a/app/Models/Solo/Score.php
+++ b/app/Models/Solo/Score.php
@@ -64,7 +64,7 @@ public static function createFromJsonOrExplode(array $params)
// older lazer builds potentially submit incorrect details here (and we still want to
// accept their scores.
if (!$score->data->passed) {
- $score->data->rank = 'D';
+ $score->data->rank = 'F';
}
$score->saveOrExplode();
diff --git a/public/images/badges/score-ranks-v2019/GradeSmall-F.svg b/public/images/badges/score-ranks-v2019/GradeSmall-F.svg
new file mode 100644
index 00000000000..aad9de51fd3
--- /dev/null
+++ b/public/images/badges/score-ranks-v2019/GradeSmall-F.svg
@@ -0,0 +1,22 @@
+
diff --git a/resources/css/bem/score-rank.less b/resources/css/bem/score-rank.less
index c968de2affb..2e57d632fce 100644
--- a/resources/css/bem/score-rank.less
+++ b/resources/css/bem/score-rank.less
@@ -19,14 +19,15 @@
background-image: url("~@images/badges/score-ranks-v2019/GradeSmall-@{filename}.svg?3");
}
- .all(XH, "SS-Silver");
- .all(X, "SS");
- .all(SH, "S-Silver");
- .all(S, "S");
- .all(A, "A");
- .all(B, "B");
- .all(C, "C");
- .all(D, "D");
+ .all(~"XH", "SS-Silver");
+ .all(~"X", "SS");
+ .all(~"SH", "S-Silver");
+ .all(~"S", "S");
+ .all(~"A", "A");
+ .all(~"B", "B");
+ .all(~"C", "C");
+ .all(~"D", "D");
+ .all(~"F", "F");
&--full {
.full-size();
diff --git a/resources/js/interfaces/rank.ts b/resources/js/interfaces/rank.ts
index f3656ea7925..8854f71d616 100644
--- a/resources/js/interfaces/rank.ts
+++ b/resources/js/interfaces/rank.ts
@@ -1,6 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.
-type Rank = 'A' | 'B' | 'C' | 'D' | 'S' | 'SH' | 'X' | 'XH';
+type Rank = 'A' | 'B' | 'C' | 'D' | 'F' | 'S' | 'SH' | 'X' | 'XH';
export default Rank;
diff --git a/resources/js/scores-show/dial.tsx b/resources/js/scores-show/dial.tsx
index 5fe4a53435f..74638f5f756 100644
--- a/resources/js/scores-show/dial.tsx
+++ b/resources/js/scores-show/dial.tsx
@@ -17,6 +17,7 @@ const displayRank: Record = {
B: 'B',
C: 'C',
D: 'D',
+ F: 'F',
S: 'S',
SH: 'S',
X: 'SS',
diff --git a/resources/js/scores-show/tower.tsx b/resources/js/scores-show/tower.tsx
index a6fd2901a9a..90878eab5f0 100644
--- a/resources/js/scores-show/tower.tsx
+++ b/resources/js/scores-show/tower.tsx
@@ -14,6 +14,7 @@ const rankIntMap: Record = {
B: 2,
C: 1,
D: 0,
+ F: -1,
S: 4,
SH: 4,
X: 5,
diff --git a/tests/Models/Solo/ScoreTest.php b/tests/Models/Solo/ScoreTest.php
index aff52ab6b38..5f300cc4dfb 100644
--- a/tests/Models/Solo/ScoreTest.php
+++ b/tests/Models/Solo/ScoreTest.php
@@ -59,7 +59,7 @@ public function testLegacyPassScoreRetainsRank()
$this->assertSame($legacy->rank, 'S');
}
- public function testLegacyFailScoreIsRankD()
+ public function testLegacyFailScoreIsRankF()
{
$score = Score::createFromJsonOrExplode([
'accuracy' => 1,
@@ -76,12 +76,12 @@ public function testLegacyFailScoreIsRankD()
]);
$this->assertFalse($score->data->passed);
- $this->assertSame($score->data->rank, 'D');
+ $this->assertSame($score->data->rank, 'F');
$legacy = $score->createLegacyEntryOrExplode();
$this->assertFalse($legacy->perfect);
- $this->assertSame($legacy->rank, 'D');
+ $this->assertSame($legacy->rank, 'F');
}
public function testLegacyScoreHitCounts()