1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Models;
7
8use Illuminate\Database\Eloquent\Relations\BelongsTo;
9
10/**
11 * @property-read string $current_start_name
12 * @property-read \App\Models\Count|null $currentStart
13 * @property-read int[] $data
14 * @property int $mode
15 * @property int $r0
16 * @property int $r1
17 * @property int $r10
18 * @property int $r11
19 * @property int $r12
20 * @property int $r13
21 * @property int $r14
22 * @property int $r15
23 * @property int $r16
24 * @property int $r17
25 * @property int $r18
26 * @property int $r19
27 * @property int $r2
28 * @property int $r20
29 * @property int $r21
30 * @property int $r22
31 * @property int $r23
32 * @property int $r24
33 * @property int $r25
34 * @property int $r26
35 * @property int $r27
36 * @property int $r28
37 * @property int $r29
38 * @property int $r3
39 * @property int $r30
40 * @property int $r31
41 * @property int $r32
42 * @property int $r33
43 * @property int $r34
44 * @property int $r35
45 * @property int $r36
46 * @property int $r37
47 * @property int $r38
48 * @property int $r39
49 * @property int $r4
50 * @property int $r40
51 * @property int $r41
52 * @property int $r42
53 * @property int $r43
54 * @property int $r44
55 * @property int $r45
56 * @property int $r46
57 * @property int $r47
58 * @property int $r48
59 * @property int $r49
60 * @property int $r5
61 * @property int $r50
62 * @property int $r51
63 * @property int $r52
64 * @property int $r53
65 * @property int $r54
66 * @property int $r55
67 * @property int $r56
68 * @property int $r57
69 * @property int $r58
70 * @property int $r59
71 * @property int $r6
72 * @property int $r60
73 * @property int $r61
74 * @property int $r62
75 * @property int $r63
76 * @property int $r64
77 * @property int $r65
78 * @property int $r66
79 * @property int $r67
80 * @property int $r68
81 * @property int $r69
82 * @property int $r7
83 * @property int $r70
84 * @property int $r71
85 * @property int $r72
86 * @property int $r73
87 * @property int $r74
88 * @property int $r75
89 * @property int $r76
90 * @property int $r77
91 * @property int $r78
92 * @property int $r79
93 * @property int $r8
94 * @property int $r80
95 * @property int $r81
96 * @property int $r82
97 * @property int $r83
98 * @property int $r84
99 * @property int $r85
100 * @property int $r86
101 * @property int $r87
102 * @property int $r88
103 * @property int $r89
104 * @property int $r9
105 * @property-read string $ruleset
106 * @property-read \App\Models\User $user
107 * @property int $user_id
108 */
109class RankHistory extends Model
110{
111 protected $table = 'osu_user_performance_rank';
112
113 public $timestamps = false;
114
115 public function __construct(array $attributes = [])
116 {
117 if ($GLOBALS['cfg']['osu']['scores']['experimental_rank_as_default']) {
118 $this->table = 'osu_user_performance_rank_exp';
119 }
120
121 parent::__construct($attributes);
122 }
123
124 public function currentStart(): BelongsTo
125 {
126 return $this->belongsTo(Count::class, 'current_start_name', 'name');
127 }
128
129 public function user()
130 {
131 return $this->belongsTo(User::class, 'user_id');
132 }
133
134 public function getCurrentStartNameAttribute(): string
135 {
136 return Count::currentRankStartName($this->ruleset);
137 }
138
139 public function getDataAttribute()
140 {
141 $data = [];
142
143 // The r$(count) may actually contain today's rank when the update
144 // process is running so it should just be ignored and use the rank
145 // from user statistics for the current rank value.
146 $startOffset = ($this->currentStart?->count ?? 0) + 1;
147 $endOffset = $startOffset + 88;
148
149 $attributes = $this->attributes;
150 for ($i = $startOffset; $i <= $endOffset; $i++) {
151 $data[] = $attributes['r'.($i % 90)] ?? 0;
152 }
153
154 $userStatistics = $this->user->statistics($this->ruleset);
155 $data[] = $userStatistics?->globalRank() ?? 0;
156
157 return $data;
158 }
159
160 public function getRulesetAttribute()
161 {
162 return Beatmap::modeStr($this->getRawAttribute('mode'));
163 }
164
165 /**
166 * Get the difference between the user's current performance rank and their
167 * performance rank as of 30 days ago.
168 *
169 * @return int|null `null` if rank history is not available at 30 days ago.
170 */
171 public function rankChangeSince30Days(): ?int
172 {
173 $data = $this->data;
174 $currentRank = $data[89];
175 $previousRank = $data[59];
176
177 return $currentRank > 0 && $previousRank > 0
178 ? $currentRank - $previousRank
179 : null;
180 }
181}