mirror of
https://github.com/ppy/osu.git
synced 2025-02-20 20:33:21 +08:00
Naming changes
This commit is contained in:
parent
5dcd4ce7c5
commit
51176e9577
@ -28,25 +28,25 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
||||
/// <summary>
|
||||
/// Evaluate the difficulty of the first note of a <see cref="MonoStreak"/>.
|
||||
/// </summary>
|
||||
public static double EvaluateDifficultyOf(MonoStreak encoding)
|
||||
public static double EvaluateDifficultyOf(MonoStreak monoStreak)
|
||||
{
|
||||
return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!) * 0.5;
|
||||
return sigmoid(monoStreak.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(monoStreak.Parent!) * 0.5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate the difficulty of the first note of a <see cref="AlternatingMonoPattern"/>.
|
||||
/// </summary>
|
||||
public static double EvaluateDifficultyOf(AlternatingMonoPattern encoding)
|
||||
public static double EvaluateDifficultyOf(AlternatingMonoPattern alternatingMonoPattern)
|
||||
{
|
||||
return sigmoid(encoding.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(encoding.Parent!);
|
||||
return sigmoid(alternatingMonoPattern.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(alternatingMonoPattern.Parent!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate the difficulty of the first note of a <see cref="RepeatingHitPatterns"/>.
|
||||
/// </summary>
|
||||
public static double EvaluateDifficultyOf(RepeatingHitPatterns encoding)
|
||||
public static double EvaluateDifficultyOf(RepeatingHitPatterns repeatingHitPattern)
|
||||
{
|
||||
return 2 * (1 - sigmoid(encoding.RepetitionInterval, 2, 2, 0.5, 1));
|
||||
return 2 * (1 - sigmoid(repeatingHitPattern.RepetitionInterval, 2, 2, 0.5, 1));
|
||||
}
|
||||
|
||||
public static double EvaluateDifficultyOf(DifficultyHitObject hitObject)
|
||||
@ -54,12 +54,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
||||
TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)hitObject).Colour;
|
||||
double difficulty = 0.0d;
|
||||
|
||||
if (colour.MonoEncoding != null) // Difficulty for MonoEncoding
|
||||
difficulty += EvaluateDifficultyOf(colour.MonoEncoding);
|
||||
if (colour.ColourEncoding != null) // Difficulty for ColourEncoding
|
||||
difficulty += EvaluateDifficultyOf(colour.ColourEncoding);
|
||||
if (colour.CoupledColourEncoding != null) // Difficulty for CoupledColourEncoding
|
||||
difficulty += EvaluateDifficultyOf(colour.CoupledColourEncoding);
|
||||
if (colour.MonoStreak != null) // Difficulty for MonoStreak
|
||||
difficulty += EvaluateDifficultyOf(colour.MonoStreak);
|
||||
if (colour.AlternatingMonoPattern != null) // Difficulty for AlternatingMonoPattern
|
||||
difficulty += EvaluateDifficultyOf(colour.AlternatingMonoPattern);
|
||||
if (colour.RepeatingHitPatterns != null) // Difficulty for RepeatingHitPattern
|
||||
difficulty += EvaluateDifficultyOf(colour.RepeatingHitPatterns);
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
/// <summary>
|
||||
/// <see cref="MonoStreak"/>s that are grouped together within this <see cref="AlternatingMonoPattern"/>.
|
||||
/// </summary>
|
||||
public readonly List<MonoStreak> Payload = new List<MonoStreak>();
|
||||
public readonly List<MonoStreak> MonoStreaks = new List<MonoStreak>();
|
||||
|
||||
/// <summary>
|
||||
/// The parent <see cref="RepeatingHitPatterns"/> that contains this <see cref="AlternatingMonoPattern"/>
|
||||
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
public RepeatingHitPatterns? Parent;
|
||||
|
||||
/// <summary>
|
||||
/// Index of this encoding within it's parent encoding
|
||||
/// Index of this <see cref="AlternatingMonoPattern"/> within it's parent <see cref="RepeatingHitPatterns"/>
|
||||
/// </summary>
|
||||
public int Index;
|
||||
|
||||
@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
public bool IsRepetitionOf(AlternatingMonoPattern other)
|
||||
{
|
||||
return HasIdenticalMonoLength(other) &&
|
||||
other.Payload.Count == Payload.Count &&
|
||||
(other.Payload[0].EncodedData[0].BaseObject as Hit)?.Type ==
|
||||
(Payload[0].EncodedData[0].BaseObject as Hit)?.Type;
|
||||
other.MonoStreaks.Count == MonoStreaks.Count &&
|
||||
(other.MonoStreaks[0].HitObjects[0].BaseObject as Hit)?.Type ==
|
||||
(MonoStreaks[0].HitObjects[0].BaseObject as Hit)?.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
/// </summary>
|
||||
public bool HasIdenticalMonoLength(AlternatingMonoPattern other)
|
||||
{
|
||||
return other.Payload[0].RunLength == Payload[0].RunLength;
|
||||
return other.MonoStreaks[0].RunLength == MonoStreaks[0].RunLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
/// <summary>
|
||||
/// List of <see cref="DifficultyHitObject"/>s that are encoded within this <see cref="MonoStreak"/>.
|
||||
/// </summary>
|
||||
public List<TaikoDifficultyHitObject> EncodedData { get; private set; } = new List<TaikoDifficultyHitObject>();
|
||||
public List<TaikoDifficultyHitObject> HitObjects { get; private set; } = new List<TaikoDifficultyHitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// The parent <see cref="AlternatingMonoPattern"/> that contains this <see cref="MonoStreak"/>
|
||||
@ -24,13 +24,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
public AlternatingMonoPattern? Parent;
|
||||
|
||||
/// <summary>
|
||||
/// Index of this encoding within it's parent encoding
|
||||
/// Index of this <see cref="MonoStreak"/> within it's parent <see cref="AlternatingMonoPattern"/>
|
||||
/// </summary>
|
||||
public int Index;
|
||||
|
||||
/// <summary>
|
||||
/// How long the mono pattern encoded within is
|
||||
/// </summary>
|
||||
public int RunLength => EncodedData.Count;
|
||||
public int RunLength => HitObjects.Count;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
/// <summary>
|
||||
/// The <see cref="AlternatingMonoPattern"/>s that are grouped together within this <see cref="RepeatingHitPatterns"/>.
|
||||
/// </summary>
|
||||
public readonly List<AlternatingMonoPattern> Payload = new List<AlternatingMonoPattern>();
|
||||
public readonly List<AlternatingMonoPattern> AlternatingMonoPatterns = new List<AlternatingMonoPattern>();
|
||||
|
||||
/// <summary>
|
||||
/// The previous <see cref="RepeatingHitPatterns"/>. This is used to determine the repetition interval.
|
||||
@ -39,24 +39,24 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if other is considered a repetition of this encoding. This is true if other's first two payloads
|
||||
/// Returns true if other is considered a repetition of this pattern. This is true if other's first two payloads
|
||||
/// have identical mono lengths.
|
||||
/// </summary>
|
||||
private bool isRepetitionOf(RepeatingHitPatterns other)
|
||||
{
|
||||
if (Payload.Count != other.Payload.Count) return false;
|
||||
if (AlternatingMonoPatterns.Count != other.AlternatingMonoPatterns.Count) return false;
|
||||
|
||||
for (int i = 0; i < Math.Min(Payload.Count, 2); i++)
|
||||
for (int i = 0; i < Math.Min(AlternatingMonoPatterns.Count, 2); i++)
|
||||
{
|
||||
if (!Payload[i].HasIdenticalMonoLength(other.Payload[i])) return false;
|
||||
if (!AlternatingMonoPatterns[i].HasIdenticalMonoLength(other.AlternatingMonoPatterns[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the closest previous <see cref="RepeatingHitPatterns"/> that has the identical <see cref="Payload"/>.
|
||||
/// Interval is defined as the amount of <see cref="RepeatingHitPatterns"/> chunks between the current and repeated encoding.
|
||||
/// Finds the closest previous <see cref="RepeatingHitPatterns"/> that has the identical <see cref="AlternatingMonoPatterns"/>.
|
||||
/// Interval is defined as the amount of <see cref="RepeatingHitPatterns"/> chunks between the current and repeated patterns.
|
||||
/// </summary>
|
||||
public void FindRepetitionInterval()
|
||||
{
|
||||
|
@ -20,30 +20,30 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
|
||||
/// </summary>
|
||||
public static void ProcessAndAssign(List<DifficultyHitObject> hitObjects)
|
||||
{
|
||||
List<RepeatingHitPatterns> encodings = encode(hitObjects);
|
||||
List<RepeatingHitPatterns> hitPatterns = encode(hitObjects);
|
||||
|
||||
// Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is
|
||||
// assigned with the relevant encodings.
|
||||
foreach (var coupledEncoding in encodings)
|
||||
foreach (var repeatingHitPattern in hitPatterns)
|
||||
{
|
||||
coupledEncoding.Payload[0].Payload[0].EncodedData[0].Colour.CoupledColourEncoding = coupledEncoding;
|
||||
repeatingHitPattern.AlternatingMonoPatterns[0].MonoStreaks[0].HitObjects[0].Colour.RepeatingHitPatterns = repeatingHitPattern;
|
||||
|
||||
// The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to
|
||||
// keep i and j for ColourEncoding's and MonoEncoding's index respectively, to keep it in line with
|
||||
// keep i and j for AlternatingMonoPattern's and MonoStreak's index respectively, to keep it in line with
|
||||
// documentation.
|
||||
for (int i = 0; i < coupledEncoding.Payload.Count; ++i)
|
||||
for (int i = 0; i < repeatingHitPattern.AlternatingMonoPatterns.Count; ++i)
|
||||
{
|
||||
AlternatingMonoPattern colourEncoding = coupledEncoding.Payload[i];
|
||||
colourEncoding.Parent = coupledEncoding;
|
||||
colourEncoding.Index = i;
|
||||
colourEncoding.Payload[0].EncodedData[0].Colour.ColourEncoding = colourEncoding;
|
||||
AlternatingMonoPattern monoPattern = repeatingHitPattern.AlternatingMonoPatterns[i];
|
||||
monoPattern.Parent = repeatingHitPattern;
|
||||
monoPattern.Index = i;
|
||||
monoPattern.MonoStreaks[0].HitObjects[0].Colour.AlternatingMonoPattern = monoPattern;
|
||||
|
||||
for (int j = 0; j < colourEncoding.Payload.Count; ++j)
|
||||
for (int j = 0; j < monoPattern.MonoStreaks.Count; ++j)
|
||||
{
|
||||
MonoStreak monoEncoding = colourEncoding.Payload[j];
|
||||
monoEncoding.Parent = colourEncoding;
|
||||
monoEncoding.Index = j;
|
||||
monoEncoding.EncodedData[0].Colour.MonoEncoding = monoEncoding;
|
||||
MonoStreak monoStreak = monoPattern.MonoStreaks[j];
|
||||
monoStreak.Parent = monoPattern;
|
||||
monoStreak.Index = j;
|
||||
monoStreak.HitObjects[0].Colour.MonoStreak = monoStreak;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,20 +54,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
|
||||
/// </summary>
|
||||
private static List<RepeatingHitPatterns> encode(List<DifficultyHitObject> data)
|
||||
{
|
||||
List<MonoStreak> firstPass = encodeMono(data);
|
||||
List<AlternatingMonoPattern> secondPass = encodeColour(firstPass);
|
||||
List<RepeatingHitPatterns> thirdPass = encodeCoupledColour(secondPass);
|
||||
List<MonoStreak> monoStreaks = encodeMonoStreak(data);
|
||||
List<AlternatingMonoPattern> alternatingMonoPatterns = encodeAlternatingMonoPattern(monoStreaks);
|
||||
List<RepeatingHitPatterns> repeatingHitPatterns = encodeRepeatingHitPattern(alternatingMonoPatterns);
|
||||
|
||||
return thirdPass;
|
||||
return repeatingHitPatterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="MonoStreak"/>s.
|
||||
/// </summary>
|
||||
private static List<MonoStreak> encodeMono(List<DifficultyHitObject> data)
|
||||
private static List<MonoStreak> encodeMonoStreak(List<DifficultyHitObject> data)
|
||||
{
|
||||
List<MonoStreak> encodings = new List<MonoStreak>();
|
||||
MonoStreak? currentEncoding = null;
|
||||
List<MonoStreak> monoStreaks = new List<MonoStreak>();
|
||||
MonoStreak? currentMonoStreak = null;
|
||||
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
{
|
||||
@ -76,92 +76,92 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
|
||||
// This ignores all non-note objects, which may or may not be the desired behaviour
|
||||
TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0);
|
||||
|
||||
// If this is the first object in the list or the colour changed, create a new mono encoding
|
||||
if (currentEncoding == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type)
|
||||
// If this is the first object in the list or the colour changed, create a new mono streak
|
||||
if (currentMonoStreak == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type)
|
||||
{
|
||||
currentEncoding = new MonoStreak();
|
||||
encodings.Add(currentEncoding);
|
||||
currentMonoStreak = new MonoStreak();
|
||||
monoStreaks.Add(currentMonoStreak);
|
||||
}
|
||||
|
||||
// Add the current object to the encoded payload.
|
||||
currentEncoding.EncodedData.Add(taikoObject);
|
||||
currentMonoStreak.HitObjects.Add(taikoObject);
|
||||
}
|
||||
|
||||
return encodings;
|
||||
return monoStreaks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a list of <see cref="MonoStreak"/>s into a list of <see cref="AlternatingMonoPattern"/>s.
|
||||
/// </summary>
|
||||
private static List<AlternatingMonoPattern> encodeColour(List<MonoStreak> data)
|
||||
private static List<AlternatingMonoPattern> encodeAlternatingMonoPattern(List<MonoStreak> data)
|
||||
{
|
||||
List<AlternatingMonoPattern> encodings = new List<AlternatingMonoPattern>();
|
||||
AlternatingMonoPattern? currentEncoding = null;
|
||||
List<AlternatingMonoPattern> monoPatterns = new List<AlternatingMonoPattern>();
|
||||
AlternatingMonoPattern? currentMonoPattern = null;
|
||||
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
{
|
||||
// Start a new ColourEncoding if the previous MonoEncoding has a different mono length, or if this is the first MonoEncoding in the list.
|
||||
if (currentEncoding == null || data[i].RunLength != data[i - 1].RunLength)
|
||||
// Start a new AlternatingMonoPattern if the previous MonoStreak has a different mono length, or if this is the first MonoStreak in the list.
|
||||
if (currentMonoPattern == null || data[i].RunLength != data[i - 1].RunLength)
|
||||
{
|
||||
currentEncoding = new AlternatingMonoPattern();
|
||||
encodings.Add(currentEncoding);
|
||||
currentMonoPattern = new AlternatingMonoPattern();
|
||||
monoPatterns.Add(currentMonoPattern);
|
||||
}
|
||||
|
||||
// Add the current MonoEncoding to the encoded payload.
|
||||
currentEncoding.Payload.Add(data[i]);
|
||||
// Add the current MonoStreak to the encoded payload.
|
||||
currentMonoPattern.MonoStreaks.Add(data[i]);
|
||||
}
|
||||
|
||||
return encodings;
|
||||
return monoPatterns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a list of <see cref="AlternatingMonoPattern"/>s into a list of <see cref="RepeatingHitPatterns"/>s.
|
||||
/// </summary>
|
||||
private static List<RepeatingHitPatterns> encodeCoupledColour(List<AlternatingMonoPattern> data)
|
||||
private static List<RepeatingHitPatterns> encodeRepeatingHitPattern(List<AlternatingMonoPattern> data)
|
||||
{
|
||||
List<RepeatingHitPatterns> encodings = new List<RepeatingHitPatterns>();
|
||||
RepeatingHitPatterns? currentEncoding = null;
|
||||
List<RepeatingHitPatterns> hitPatterns = new List<RepeatingHitPatterns>();
|
||||
RepeatingHitPatterns? currentHitPattern = null;
|
||||
|
||||
for (int i = 0; i < data.Count; i++)
|
||||
{
|
||||
// Start a new CoupledColourEncoding. ColourEncodings that should be grouped together will be handled later within this loop.
|
||||
currentEncoding = new RepeatingHitPatterns(currentEncoding);
|
||||
// Start a new RepeatingHitPattern. AlternatingMonoPatterns that should be grouped together will be handled later within this loop.
|
||||
currentHitPattern = new RepeatingHitPatterns(currentHitPattern);
|
||||
|
||||
// Determine if future ColourEncodings should be grouped.
|
||||
// Determine if future AlternatingMonoPatterns should be grouped.
|
||||
bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]);
|
||||
|
||||
if (!isCoupled)
|
||||
{
|
||||
// If not, add the current ColourEncoding to the encoded payload and continue.
|
||||
currentEncoding.Payload.Add(data[i]);
|
||||
// If not, add the current AlternatingMonoPattern to the encoded payload and continue.
|
||||
currentHitPattern.AlternatingMonoPatterns.Add(data[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If so, add the current ColourEncoding to the encoded payload and start repeatedly checking if the
|
||||
// subsequent ColourEncodings should be grouped by increasing i and doing the appropriate isCoupled check.
|
||||
// If so, add the current AlternatingMonoPattern to the encoded payload and start repeatedly checking if the
|
||||
// subsequent AlternatingMonoPatterns should be grouped by increasing i and doing the appropriate isCoupled check.
|
||||
while (isCoupled)
|
||||
{
|
||||
currentEncoding.Payload.Add(data[i]);
|
||||
currentHitPattern.AlternatingMonoPatterns.Add(data[i]);
|
||||
i++;
|
||||
isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]);
|
||||
}
|
||||
|
||||
// Skip over viewed data and add the rest to the payload
|
||||
currentEncoding.Payload.Add(data[i]);
|
||||
currentEncoding.Payload.Add(data[i + 1]);
|
||||
currentHitPattern.AlternatingMonoPatterns.Add(data[i]);
|
||||
currentHitPattern.AlternatingMonoPatterns.Add(data[i + 1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
encodings.Add(currentEncoding);
|
||||
hitPatterns.Add(currentHitPattern);
|
||||
}
|
||||
|
||||
// Final pass to find repetition intervals
|
||||
for (int i = 0; i < encodings.Count; i++)
|
||||
for (int i = 0; i < hitPatterns.Count; i++)
|
||||
{
|
||||
encodings[i].FindRepetitionInterval();
|
||||
hitPatterns[i].FindRepetitionInterval();
|
||||
}
|
||||
|
||||
return encodings;
|
||||
return hitPatterns;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,18 +11,18 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
|
||||
public class TaikoDifficultyHitObjectColour
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="MonoEncoding"/> that encodes this note, only present if this is the first note within a <see cref="MonoEncoding"/>
|
||||
/// The <see cref="MonoStreak"/> that encodes this note, only present if this is the first note within a <see cref="MonoStreak"/>
|
||||
/// </summary>
|
||||
public MonoStreak? MonoEncoding;
|
||||
public MonoStreak? MonoStreak;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ColourEncoding"/> that encodes this note, only present if this is the first note within a <see cref="ColourEncoding"/>
|
||||
/// The <see cref="AlternatingMonoPattern"/> that encodes this note, only present if this is the first note within a <see cref="AlternatingMonoPattern"/>
|
||||
/// </summary>
|
||||
public AlternatingMonoPattern? ColourEncoding;
|
||||
public AlternatingMonoPattern? AlternatingMonoPattern;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CoupledColourEncoding"/> that encodes this note, only present if this is the first note within a <see cref="CoupledColourEncoding"/>
|
||||
/// The <see cref="RepeatingHitPatterns"/> that encodes this note, only present if this is the first note within a <see cref="RepeatingHitPatterns"/>
|
||||
/// </summary>
|
||||
public RepeatingHitPatterns? CoupledColourEncoding;
|
||||
public RepeatingHitPatterns? RepeatingHitPatterns;
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
{
|
||||
protected override double SkillMultiplier => 0.12;
|
||||
|
||||
// This is set to decay slower than other skills, due to the fact that only the first note of each Mono/Colour/Coupled
|
||||
// encoding having any difficulty values, and we want to allow colour difficulty to be able to build up even on
|
||||
// This is set to decay slower than other skills, due to the fact that only the first note of each encoding class
|
||||
// having any difficulty values, and we want to allow colour difficulty to be able to build up even on
|
||||
// slower maps.
|
||||
protected override double StrainDecayBase => 0.8;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user