mirror of
https://github.com/ppy/osu.git
synced 2025-01-26 23:43:03 +08:00
Merge remote-tracking branch 'upstream/master' into ranks-section
This commit is contained in:
commit
520b806305
23
.vscode/launch.json
vendored
23
.vscode/launch.json
vendored
@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [{
|
||||
"name": "osu! (VisualTests)",
|
||||
"name": "osu! VisualTests (Debug)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
@ -18,7 +18,24 @@
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "osu! (debug)",
|
||||
"name": "osu! VisualTests (Release)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/osu.Game/bin/Release/osu!.exe",
|
||||
"args": [
|
||||
"--tests"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "osu! (Debug)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
@ -32,7 +49,7 @@
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "osu! (release)",
|
||||
"name": "osu! (Release)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
|
1
.vscode/tasks.json
vendored
1
.vscode/tasks.json
vendored
@ -23,6 +23,7 @@
|
||||
},
|
||||
{
|
||||
"taskName": "Build (Release)",
|
||||
"group": "build",
|
||||
"args": [
|
||||
"/property:Configuration=Release"
|
||||
],
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e1352a8b0b5d1ba8acd9335a56c714d2ccc2f6a6
|
||||
Subproject commit 3760443ea9bf682a1bbf6cfa6aa00ea9541c12aa
|
@ -5,9 +5,9 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
@ -17,14 +17,37 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
protected override IEnumerable<CatchBaseHit> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
||||
{
|
||||
if (!(obj is IHasXPosition))
|
||||
var curveData = obj as IHasCurve;
|
||||
var positionData = obj as IHasPosition;
|
||||
var comboData = obj as IHasCombo;
|
||||
|
||||
if (positionData == null)
|
||||
yield break;
|
||||
|
||||
if (curveData != null)
|
||||
{
|
||||
yield return new JuiceStream
|
||||
{
|
||||
StartTime = obj.StartTime,
|
||||
Samples = obj.Samples,
|
||||
ControlPoints = curveData.ControlPoints,
|
||||
CurveType = curveData.CurveType,
|
||||
Distance = curveData.Distance,
|
||||
RepeatSamples = curveData.RepeatSamples,
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
X = positionData.X / CatchPlayfield.BASE_WIDTH,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
};
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return new Fruit
|
||||
{
|
||||
StartTime = obj.StartTime,
|
||||
NewCombo = (obj as IHasCombo)?.NewCombo ?? false,
|
||||
X = ((IHasXPosition)obj).X / OsuPlayfield.BASE_SIZE.X
|
||||
Samples = obj.Samples,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
X = positionData.X / CatchPlayfield.BASE_WIDTH
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
|
||||
public class CatchModHidden : ModHidden
|
||||
{
|
||||
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
|
||||
public override string Description => @"Play with fading notes for a slight score advantage.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
|
||||
where TObject : CatchBaseHit
|
||||
{
|
||||
public new TObject HitObject;
|
||||
|
||||
protected DrawableCatchHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchBaseHit>
|
||||
{
|
||||
protected DrawableCatchHitObject(CatchBaseHit hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativePositionAxes = Axes.Both;
|
||||
X = hitObject.X;
|
||||
Y = (float)HitObject.StartTime;
|
||||
}
|
||||
|
||||
public Func<CatchBaseHit, bool> CheckPosition;
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (timeOffset > 0)
|
||||
AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss });
|
||||
}
|
||||
|
||||
private const float preempt = 1000;
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
using (BeginAbsoluteSequence(HitObject.StartTime - preempt))
|
||||
{
|
||||
// animation
|
||||
this.FadeIn(200);
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Miss:
|
||||
using (BeginAbsoluteSequence(HitObject.StartTime, true))
|
||||
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
Normal file
34
osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableDroplet : DrawableCatchHitObject<Droplet>
|
||||
{
|
||||
public DrawableDroplet(Droplet h)
|
||||
: base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Size = new Vector2(Pulp.PULP_SIZE);
|
||||
|
||||
AccentColour = h.ComboColour;
|
||||
Masking = false;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = new Pulp
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
Scale = new Vector2(0.8f),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +1,29 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableFruit : DrawableScrollingHitObject<CatchBaseHit>
|
||||
public class DrawableFruit : DrawableCatchHitObject<Fruit>
|
||||
{
|
||||
private const float pulp_size = 20;
|
||||
|
||||
private class Pulp : Circle, IHasAccentColour
|
||||
{
|
||||
public Pulp()
|
||||
{
|
||||
Size = new Vector2(pulp_size);
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Colour = Color4.White.Opacity(0.9f);
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get { return accentColour; }
|
||||
set
|
||||
{
|
||||
accentColour = value;
|
||||
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
Colour = accentColour.Lighten(100),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public DrawableFruit(CatchBaseHit h)
|
||||
public DrawableFruit(Fruit h)
|
||||
: base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2(pulp_size * 2.2f, pulp_size * 2.8f);
|
||||
|
||||
RelativePositionAxes = Axes.Both;
|
||||
X = h.X;
|
||||
|
||||
Size = new Vector2(Pulp.PULP_SIZE * 2.2f, Pulp.PULP_SIZE * 2.8f);
|
||||
AccentColour = HitObject.ComboColour;
|
||||
|
||||
Masking = false;
|
||||
|
||||
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||
}
|
||||
|
||||
public Func<CatchBaseHit, bool> CheckPosition;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -114,30 +71,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private const float preempt = 1000;
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (timeOffset > 0)
|
||||
AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss });
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
using (BeginAbsoluteSequence(HitObject.StartTime - preempt))
|
||||
{
|
||||
// animation
|
||||
this.FadeIn(200);
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Miss:
|
||||
using (BeginAbsoluteSequence(HitObject.StartTime, true))
|
||||
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableJuiceStream : DrawableCatchHitObject<JuiceStream>
|
||||
{
|
||||
private readonly Container dropletContainer;
|
||||
|
||||
public DrawableJuiceStream(JuiceStream s) : base(s)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Height = (float)HitObject.Duration;
|
||||
X = 0;
|
||||
|
||||
Child = dropletContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
|
||||
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
||||
};
|
||||
|
||||
foreach (CatchBaseHit tick in s.Ticks)
|
||||
{
|
||||
TinyDroplet tiny = tick as TinyDroplet;
|
||||
if (tiny != null)
|
||||
{
|
||||
AddNested(new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) });
|
||||
continue;
|
||||
}
|
||||
|
||||
Droplet droplet = tick as Droplet;
|
||||
if (droplet != null)
|
||||
AddNested(new DrawableDroplet(droplet));
|
||||
|
||||
Fruit fruit = tick as Fruit;
|
||||
if (fruit != null)
|
||||
AddNested(new DrawableFruit(fruit));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddNested(DrawableHitObject<CatchBaseHit> h)
|
||||
{
|
||||
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||
dropletContainer.Add(h);
|
||||
base.AddNested(h);
|
||||
}
|
||||
}
|
||||
}
|
43
osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
Normal file
43
osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
||||
{
|
||||
public class Pulp : Circle, IHasAccentColour
|
||||
{
|
||||
public const float PULP_SIZE = 20;
|
||||
|
||||
public Pulp()
|
||||
{
|
||||
Size = new Vector2(PULP_SIZE);
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Colour = Color4.White.Opacity(0.9f);
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get { return accentColour; }
|
||||
set
|
||||
{
|
||||
accentColour = value;
|
||||
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 5,
|
||||
Colour = accentColour.Lighten(100),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
169
osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
Normal file
169
osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using osu.Framework.Lists;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class JuiceStream : CatchBaseHit, IHasCurve
|
||||
{
|
||||
/// <summary>
|
||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public readonly SliderCurve Curve = new SliderCurve();
|
||||
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
||||
|
||||
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
||||
|
||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
public IEnumerable<CatchBaseHit> Ticks
|
||||
{
|
||||
get
|
||||
{
|
||||
SortedList<CatchBaseHit> ticks = new SortedList<CatchBaseHit>((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||
|
||||
if (TickDistance == 0)
|
||||
return ticks;
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
ticks.Add(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = StartTime,
|
||||
X = X
|
||||
});
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
|
||||
ticks.Add(new Droplet
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
double tinyTickInterval = tickDistance / length * repeatDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < repeatDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration;
|
||||
|
||||
ticks.Add(new TinyDroplet
|
||||
{
|
||||
StartTime = repeatStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
ticks.Add(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = repeatStartTime + repeatDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
}
|
||||
|
||||
return ticks;
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
|
||||
public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public double Distance
|
||||
{
|
||||
get { return Curve.Distance; }
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
get { return Curve.ControlPoints; }
|
||||
set { Curve.ControlPoints = value; }
|
||||
}
|
||||
|
||||
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
|
||||
|
||||
public CurveType CurveType
|
||||
{
|
||||
get { return Curve.CurveType; }
|
||||
set { Curve.CurveType = value; }
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
double p = progress * RepeatCount % 1;
|
||||
if (RepeatAt(progress) % 2 == 1)
|
||||
p = 1 - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
|
||||
}
|
||||
}
|
9
osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
Normal file
9
osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class TinyDroplet : Droplet
|
||||
{
|
||||
}
|
||||
}
|
@ -21,6 +21,19 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
{
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
var stream = obj as JuiceStream;
|
||||
|
||||
if (stream != null)
|
||||
{
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
foreach (var unused in stream.Ticks)
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var fruit = obj as Fruit;
|
||||
|
||||
if (fruit != null)
|
||||
|
@ -2,22 +2,14 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
protected override Beatmap CreateBeatmap()
|
||||
public TestCaseCatchPlayer() : base(typeof(CatchRuleset))
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs
Normal file
27
osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchStacker() : base(typeof(CatchRuleset))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Beatmap CreateBeatmap()
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatcherArea),
|
||||
typeof(Catcher),
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
new CatchInputManager(rulesets.GetRuleset(2))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new CatcherArea
|
||||
Child = new Catcher
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -1,10 +1,12 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -13,16 +15,20 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatchPlayfield : ScrollingPlayfield
|
||||
{
|
||||
public static readonly float BASE_WIDTH = 512;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
private readonly CatcherArea catcherArea;
|
||||
|
||||
private readonly Container catcherContainer;
|
||||
private readonly Catcher catcher;
|
||||
|
||||
public CatchPlayfield()
|
||||
: base(Axes.Y)
|
||||
{
|
||||
Reversed.Value = true;
|
||||
Container explodingFruitContainer;
|
||||
|
||||
Size = new Vector2(1);
|
||||
Reversed.Value = true;
|
||||
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
@ -33,24 +39,43 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
catcherArea = new CatcherArea
|
||||
explodingFruitContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
catcherContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Height = 0.3f
|
||||
Height = 180,
|
||||
Child = catcher = new Catcher
|
||||
{
|
||||
ExplodingFruitTarget = explodingFruitContainer,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Origin = Anchor.TopCentre,
|
||||
X = 0.5f,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
catcher.Size = new Vector2(catcherContainer.DrawSize.Y);
|
||||
}
|
||||
|
||||
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2;
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
h.Depth = (float)h.HitObject.StartTime;
|
||||
|
||||
base.Add(h);
|
||||
|
||||
var fruit = (DrawableFruit)h;
|
||||
fruit.CheckPosition = catcherArea.CheckIfWeCanCatch;
|
||||
var fruit = (DrawableCatchHitObject)h;
|
||||
fruit.CheckPosition = CheckIfWeCanCatch;
|
||||
}
|
||||
|
||||
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
@ -58,8 +83,12 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
if (judgement.IsHit)
|
||||
{
|
||||
Vector2 screenPosition = judgedObject.ScreenSpaceDrawQuad.Centre;
|
||||
Remove(judgedObject);
|
||||
catcherArea.Add(judgedObject, screenPosition);
|
||||
|
||||
// todo: don't do this
|
||||
(judgedObject.Parent as Container<DrawableHitObject>)?.Remove(judgedObject);
|
||||
(judgedObject.Parent as Container)?.Remove(judgedObject);
|
||||
|
||||
catcher.Add(judgedObject, screenPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h)
|
||||
{
|
||||
if (h is Fruit)
|
||||
return new DrawableFruit(h);
|
||||
var fruit = h as Fruit;
|
||||
if (fruit != null)
|
||||
return new DrawableFruit(fruit);
|
||||
|
||||
var stream = h as JuiceStream;
|
||||
if (stream != null)
|
||||
return new DrawableJuiceStream(stream);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
193
osu.Game.Rulesets.Catch/UI/Catcher.cs
Normal file
193
osu.Game.Rulesets.Catch/UI/Catcher.cs
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
private Texture texture;
|
||||
|
||||
private Container<DrawableHitObject> caughtFruit;
|
||||
|
||||
public Container ExplodingFruitTarget;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
createCatcherSprite(),
|
||||
caughtFruit = new Container<DrawableHitObject>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int currentDirection;
|
||||
|
||||
private bool dashing;
|
||||
|
||||
protected bool Dashing
|
||||
{
|
||||
get { return dashing; }
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
|
||||
dashing = value;
|
||||
|
||||
if (dashing)
|
||||
Schedule(addAdditiveSprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdditiveSprite()
|
||||
{
|
||||
if (!dashing) return;
|
||||
|
||||
var additive = createCatcherSprite();
|
||||
|
||||
additive.RelativePositionAxes = Axes.Both;
|
||||
additive.Blending = BlendingMode.Additive;
|
||||
additive.Position = Position;
|
||||
additive.Scale = Scale;
|
||||
|
||||
((Container)Parent).Add(additive);
|
||||
|
||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
||||
|
||||
Scheduler.AddDelayed(addAdditiveSprite, 50);
|
||||
}
|
||||
|
||||
private Sprite createCatcherSprite() => new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
Texture = texture,
|
||||
OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly.
|
||||
};
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||
/// </summary>
|
||||
private const double base_speed = 1.0 / 512;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
double dashModifier = Dashing ? 1 : 0.5;
|
||||
|
||||
Scale = new Vector2(Math.Sign(currentDirection), 1);
|
||||
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1);
|
||||
}
|
||||
|
||||
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
|
||||
{
|
||||
fruit.RelativePositionAxes = Axes.None;
|
||||
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
|
||||
|
||||
fruit.Anchor = Anchor.TopCentre;
|
||||
fruit.Origin = Anchor.BottomCentre;
|
||||
fruit.Scale *= 0.7f;
|
||||
fruit.LifetimeEnd = double.MaxValue;
|
||||
|
||||
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
||||
|
||||
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
|
||||
{
|
||||
fruit.X += RNG.Next(-5, 5);
|
||||
fruit.Y -= RNG.Next(0, 5);
|
||||
}
|
||||
|
||||
caughtFruit.Add(fruit);
|
||||
|
||||
if (((CatchBaseHit)fruit.HitObject).LastInCombo)
|
||||
explode();
|
||||
}
|
||||
|
||||
private void explode()
|
||||
{
|
||||
var fruit = caughtFruit.ToArray();
|
||||
|
||||
foreach (var f in fruit)
|
||||
{
|
||||
var originalX = f.X * Scale.X;
|
||||
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
f.Anchor = Anchor.TopLeft;
|
||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
caughtFruit.Remove(f);
|
||||
|
||||
ExplodingFruitTarget.Add(f);
|
||||
}
|
||||
|
||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
||||
.Then()
|
||||
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||
|
||||
f.MoveToX(f.X + originalX * 6, 1000);
|
||||
f.FadeOut(750);
|
||||
|
||||
f.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherArea : Container
|
||||
{
|
||||
private Catcher catcher;
|
||||
private Container explodingFruitContainer;
|
||||
|
||||
public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition);
|
||||
|
||||
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
explodingFruitContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
catcher = new Catcher
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
ExplodingFruitTarget = explodingFruitContainer,
|
||||
Origin = Anchor.TopCentre,
|
||||
X = 0.5f,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
catcher.Size = new Vector2(DrawSize.Y);
|
||||
}
|
||||
|
||||
private class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
private Texture texture;
|
||||
|
||||
private Container<DrawableHitObject> caughtFruit;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
createCatcherSprite(),
|
||||
caughtFruit = new Container<DrawableHitObject>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int currentDirection;
|
||||
|
||||
private bool dashing;
|
||||
|
||||
public Container ExplodingFruitTarget;
|
||||
|
||||
protected bool Dashing
|
||||
{
|
||||
get { return dashing; }
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
|
||||
dashing = value;
|
||||
|
||||
if (dashing)
|
||||
Schedule(addAdditiveSprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdditiveSprite()
|
||||
{
|
||||
if (!dashing) return;
|
||||
|
||||
var additive = createCatcherSprite();
|
||||
|
||||
additive.RelativePositionAxes = Axes.Both;
|
||||
additive.Blending = BlendingMode.Additive;
|
||||
additive.Position = Position;
|
||||
additive.Scale = Scale;
|
||||
|
||||
((CatcherArea)Parent).Add(additive);
|
||||
|
||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
||||
|
||||
Scheduler.AddDelayed(addAdditiveSprite, 50);
|
||||
}
|
||||
|
||||
private Sprite createCatcherSprite() => new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
Texture = texture,
|
||||
OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly.
|
||||
};
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||
/// </summary>
|
||||
private const double base_speed = 1.0 / 512;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
double dashModifier = Dashing ? 1 : 0.5;
|
||||
|
||||
Scale = new Vector2(Math.Sign(currentDirection), 1);
|
||||
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1);
|
||||
}
|
||||
|
||||
public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition)
|
||||
{
|
||||
fruit.RelativePositionAxes = Axes.None;
|
||||
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
|
||||
|
||||
fruit.Anchor = Anchor.TopCentre;
|
||||
fruit.Origin = Anchor.BottomCentre;
|
||||
fruit.Scale *= 0.7f;
|
||||
fruit.LifetimeEnd = double.MaxValue;
|
||||
|
||||
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
||||
|
||||
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
|
||||
{
|
||||
fruit.X += RNG.Next(-5, 5);
|
||||
fruit.Y -= RNG.Next(0, 5);
|
||||
}
|
||||
|
||||
caughtFruit.Add(fruit);
|
||||
|
||||
if (((CatchBaseHit)fruit.HitObject).LastInCombo)
|
||||
explode();
|
||||
}
|
||||
|
||||
private void explode()
|
||||
{
|
||||
var fruit = caughtFruit.ToArray();
|
||||
|
||||
foreach (var f in fruit)
|
||||
{
|
||||
var originalX = f.X * Scale.X;
|
||||
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
f.Anchor = Anchor.TopLeft;
|
||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
caughtFruit.Remove(f);
|
||||
|
||||
ExplodingFruitTarget.Add(f);
|
||||
}
|
||||
|
||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
||||
.Then()
|
||||
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||
|
||||
f.MoveToX(f.X + originalX * 6, 1000);
|
||||
f.FadeOut(750);
|
||||
|
||||
f.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -50,16 +50,23 @@
|
||||
<Compile Include="Beatmaps\CatchBeatmapProcessor.cs" />
|
||||
<Compile Include="CatchDifficultyCalculator.cs" />
|
||||
<Compile Include="CatchInputManager.cs" />
|
||||
<Compile Include="Objects\Drawable\DrawableCatchHitObject.cs" />
|
||||
<Compile Include="Objects\Drawable\DrawableDroplet.cs" />
|
||||
<Compile Include="Objects\Drawable\DrawableJuiceStream.cs" />
|
||||
<Compile Include="Objects\Drawable\Pieces\Pulp.cs" />
|
||||
<Compile Include="Objects\JuiceStream.cs" />
|
||||
<Compile Include="Scoring\CatchScoreProcessor.cs" />
|
||||
<Compile Include="Judgements\CatchJudgement.cs" />
|
||||
<Compile Include="Objects\CatchBaseHit.cs" />
|
||||
<Compile Include="Objects\Drawable\DrawableFruit.cs" />
|
||||
<Compile Include="Objects\Droplet.cs" />
|
||||
<Compile Include="Objects\Fruit.cs" />
|
||||
<Compile Include="Objects\TinyDroplet.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tests\TestCaseCatcher.cs" />
|
||||
<Compile Include="Tests\TestCaseCatchStacker.cs" />
|
||||
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
|
||||
<Compile Include="UI\CatcherArea.cs" />
|
||||
<Compile Include="UI\Catcher.cs" />
|
||||
<Compile Include="UI\CatchRulesetContainer.cs" />
|
||||
<Compile Include="UI\CatchPlayfield.cs" />
|
||||
<Compile Include="CatchRuleset.cs" />
|
||||
@ -79,11 +86,6 @@
|
||||
<Name>osu.Framework</Name>
|
||||
<Private>False</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
|
||||
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
|
||||
<Name>osu.Game.Rulesets.Osu</Name>
|
||||
<Private>False</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
|
||||
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
|
||||
<Name>osu.Game</Name>
|
||||
|
@ -10,7 +10,7 @@ using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Testing
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
internal class TestCaseManiaHitObjects : OsuTestCase
|
@ -17,7 +17,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Testing
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
internal class TestCaseManiaPlayfield : OsuTestCase
|
@ -80,8 +80,8 @@
|
||||
<Compile Include="Objects\Note.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ManiaInputManager.cs" />
|
||||
<Compile Include="Testing\TestCaseManiaHitObjects.cs" />
|
||||
<Compile Include="Testing\TestCaseManiaPlayfield.cs" />
|
||||
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
|
||||
<Compile Include="Timing\GravityScrollingContainer.cs" />
|
||||
<Compile Include="Timing\ScrollingAlgorithm.cs" />
|
||||
<Compile Include="UI\Column.cs" />
|
||||
|
@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public override bool DisplayJudgement => false;
|
||||
|
||||
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
|
||||
{
|
||||
this.sliderTick = sliderTick;
|
||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private readonly SpinnerDisc disc;
|
||||
private readonly SpinnerTicks ticks;
|
||||
private readonly SpinnerSpmCounter spmCounter;
|
||||
|
||||
private readonly Container mainContainer;
|
||||
|
||||
@ -103,6 +104,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
},
|
||||
}
|
||||
},
|
||||
spmCounter = new SpinnerSpmCounter
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Y = 120,
|
||||
Alpha = 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -157,6 +165,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
protected override void Update()
|
||||
{
|
||||
disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
|
||||
if (!spmCounter.IsPresent && disc.Tracking)
|
||||
spmCounter.FadeIn(TIME_FADEIN);
|
||||
|
||||
base.Update();
|
||||
}
|
||||
@ -167,6 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
circle.Rotation = disc.Rotation;
|
||||
ticks.Rotation = disc.Rotation;
|
||||
spmCounter.SetRotation(disc.RotationAbsolute);
|
||||
|
||||
float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
|
||||
disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
|
||||
|
@ -77,7 +77,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
private float lastAngle;
|
||||
private float currentRotation;
|
||||
public float RotationAbsolute;
|
||||
|
||||
private int completeTick;
|
||||
|
||||
private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360));
|
||||
|
@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class SpinnerSpmCounter : Container
|
||||
{
|
||||
private readonly OsuSpriteText spmText;
|
||||
|
||||
public SpinnerSpmCounter()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
spmText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = @"0",
|
||||
Font = @"Venera",
|
||||
TextSize = 24
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = @"SPINS PER MINUTE",
|
||||
Font = @"Venera",
|
||||
TextSize = 12,
|
||||
Y = 30
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private double spm;
|
||||
|
||||
public double SpinsPerMinute
|
||||
{
|
||||
get { return spm; }
|
||||
private set
|
||||
{
|
||||
if (value == spm) return;
|
||||
spm = value;
|
||||
spmText.Text = Math.Truncate(value).ToString(@"#0");
|
||||
}
|
||||
}
|
||||
|
||||
private struct RotationRecord
|
||||
{
|
||||
public float Rotation;
|
||||
public double Time;
|
||||
}
|
||||
|
||||
private readonly Queue<RotationRecord> records = new Queue<RotationRecord>();
|
||||
private const double spm_count_duration = 595; // not using hundreds to avoid frame rounding issues
|
||||
|
||||
public void SetRotation(float currentRotation)
|
||||
{
|
||||
if (records.Count > 0)
|
||||
{
|
||||
var record = records.Peek();
|
||||
while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration)
|
||||
record = records.Dequeue();
|
||||
SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360;
|
||||
}
|
||||
|
||||
records.Enqueue(new RotationRecord { Rotation = currentRotation, Time = Time.Current });
|
||||
}
|
||||
}
|
||||
}
|
@ -91,6 +91,9 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
var osuJudgement = (OsuJudgement)judgement;
|
||||
var osuObject = (OsuHitObject)judgedObject.HitObject;
|
||||
|
||||
if (!judgedObject.DisplayJudgement)
|
||||
return;
|
||||
|
||||
DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement)
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -68,6 +68,7 @@
|
||||
<Compile Include="Objects\Drawables\Pieces\RingPiece.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SliderBouncer.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SpinnerDisc.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SpinnerSpmCounter.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SpinnerTicks.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
|
||||
|
@ -55,14 +55,17 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
|
||||
|
||||
// Post processing step to transform hit objects with the same start time into strong hits
|
||||
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
|
||||
if (original.BeatmapInfo.RulesetID == 3)
|
||||
{
|
||||
TaikoHitObject first = x.First();
|
||||
if (x.Skip(1).Any())
|
||||
first.IsStrong = true;
|
||||
return first;
|
||||
}).ToList();
|
||||
// Post processing step to transform mania hit objects with the same start time into strong hits
|
||||
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
|
||||
{
|
||||
TaikoHitObject first = x.First();
|
||||
if (x.Skip(1).Any())
|
||||
first.IsStrong = true;
|
||||
return first;
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
FillMode = FillMode.Fit;
|
||||
}
|
||||
|
||||
public override bool DisplayJudgement => false;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
@ -221,7 +221,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
if (judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
|
||||
if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
|
||||
{
|
||||
judgementContainer.Add(new DrawableTaikoJudgement(judgedObject, judgement)
|
||||
{
|
||||
|
146
osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs
Normal file
146
osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Tests.Resources;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
[TestFixture]
|
||||
public class OsuLegacyDecoderTest
|
||||
{
|
||||
[Test]
|
||||
public void TestDecodeMetadata()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
var meta = beatmap.BeatmapInfo.Metadata;
|
||||
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
||||
Assert.AreEqual("Soleily", meta.Artist);
|
||||
Assert.AreEqual("Soleily", meta.ArtistUnicode);
|
||||
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
|
||||
Assert.AreEqual("Gamu", meta.Author);
|
||||
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
|
||||
Assert.AreEqual(164471, meta.PreviewTime);
|
||||
Assert.AreEqual(string.Empty, meta.Source);
|
||||
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
|
||||
Assert.AreEqual("Renatus", meta.Title);
|
||||
Assert.AreEqual("Renatus", meta.TitleUnicode);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeGeneral()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
|
||||
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
|
||||
Assert.AreEqual(false, beatmapInfo.Countdown);
|
||||
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
|
||||
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
|
||||
Assert.IsTrue(beatmapInfo.RulesetID == 0);
|
||||
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
|
||||
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeEditor()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
|
||||
int[] expectedBookmarks =
|
||||
{
|
||||
11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351,
|
||||
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
||||
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
||||
};
|
||||
Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length);
|
||||
for (int i = 0; i < expectedBookmarks.Length; i++)
|
||||
Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]);
|
||||
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
|
||||
Assert.AreEqual(4, beatmap.BeatDivisor);
|
||||
Assert.AreEqual(4, beatmap.GridSize);
|
||||
Assert.AreEqual(2, beatmap.TimelineZoom);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeDifficulty()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
var difficulty = beatmap.BeatmapInfo.Difficulty;
|
||||
Assert.AreEqual(6.5f, difficulty.DrainRate);
|
||||
Assert.AreEqual(4, difficulty.CircleSize);
|
||||
Assert.AreEqual(8, difficulty.OverallDifficulty);
|
||||
Assert.AreEqual(9, difficulty.ApproachRate);
|
||||
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
|
||||
Assert.AreEqual(2, difficulty.SliderTickRate);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeColors()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
Color4[] expected =
|
||||
{
|
||||
new Color4(142, 199, 255, 255),
|
||||
new Color4(255, 128, 128, 255),
|
||||
new Color4(128, 255, 255, 255),
|
||||
new Color4(128, 255, 128, 255),
|
||||
new Color4(255, 187, 255, 255),
|
||||
new Color4(255, 177, 140, 255),
|
||||
};
|
||||
Assert.AreEqual(expected.Length, beatmap.ComboColors.Count);
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
Assert.AreEqual(expected[i], beatmap.ComboColors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeHitObjects()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
|
||||
var curveData = beatmap.HitObjects[0] as IHasCurve;
|
||||
var positionData = beatmap.HitObjects[0] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.IsNotNull(curveData);
|
||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
||||
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
|
||||
Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
|
||||
|
||||
positionData = beatmap.HitObjects[1] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
||||
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
|
||||
Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
164
osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
Normal file
164
osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.IPC;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.IO
|
||||
{
|
||||
[TestFixture]
|
||||
public class ImportBeatmapTest
|
||||
{
|
||||
private const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz";
|
||||
|
||||
[Test]
|
||||
public void TestImportWhenClosed()
|
||||
{
|
||||
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
|
||||
using (HeadlessGameHost host = new HeadlessGameHost("TestImportWhenClosed"))
|
||||
{
|
||||
var osu = loadOsu(host);
|
||||
|
||||
var temp = prepareTempCopy(osz_path);
|
||||
|
||||
Assert.IsTrue(File.Exists(temp));
|
||||
|
||||
osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||
|
||||
ensureLoaded(osu);
|
||||
|
||||
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImportOverIPC()
|
||||
{
|
||||
using (HeadlessGameHost host = new HeadlessGameHost("host", true))
|
||||
using (HeadlessGameHost client = new HeadlessGameHost("client", true))
|
||||
{
|
||||
Assert.IsTrue(host.IsPrimaryInstance);
|
||||
Assert.IsTrue(!client.IsPrimaryInstance);
|
||||
|
||||
var osu = loadOsu(host);
|
||||
|
||||
var temp = prepareTempCopy(osz_path);
|
||||
|
||||
Assert.IsTrue(File.Exists(temp));
|
||||
|
||||
var importer = new BeatmapIPCChannel(client);
|
||||
if (!importer.ImportAsync(temp).Wait(10000))
|
||||
Assert.Fail(@"IPC took too long to send");
|
||||
|
||||
ensureLoaded(osu);
|
||||
|
||||
waitForOrAssert(() => !File.Exists(temp), "Temporary still exists after IPC import", 5000);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImportWhenFileOpen()
|
||||
{
|
||||
using (HeadlessGameHost host = new HeadlessGameHost("TestImportWhenFileOpen"))
|
||||
{
|
||||
var osu = loadOsu(host);
|
||||
|
||||
var temp = prepareTempCopy(osz_path);
|
||||
|
||||
Assert.IsTrue(File.Exists(temp), "Temporary file copy never substantiated");
|
||||
|
||||
using (File.OpenRead(temp))
|
||||
osu.Dependencies.Get<BeatmapManager>().Import(temp);
|
||||
|
||||
ensureLoaded(osu);
|
||||
|
||||
File.Delete(temp);
|
||||
|
||||
Assert.IsFalse(File.Exists(temp), "We likely held a read lock on the file when we shouldn't");
|
||||
}
|
||||
}
|
||||
|
||||
private string prepareTempCopy(string path)
|
||||
{
|
||||
var temp = Path.GetTempFileName();
|
||||
return new FileInfo(path).CopyTo(temp, true).FullName;
|
||||
}
|
||||
|
||||
private OsuGameBase loadOsu(GameHost host)
|
||||
{
|
||||
host.Storage.DeleteDatabase(@"client");
|
||||
|
||||
var osu = new OsuGameBase();
|
||||
Task.Run(() => host.Run(osu));
|
||||
|
||||
waitForOrAssert(() => osu.IsLoaded, @"osu! failed to start in a reasonable amount of time");
|
||||
|
||||
return osu;
|
||||
}
|
||||
|
||||
private void ensureLoaded(OsuGameBase osu, int timeout = 60000)
|
||||
{
|
||||
IEnumerable<BeatmapSetInfo> resultSets = null;
|
||||
|
||||
var store = osu.Dependencies.Get<BeatmapManager>();
|
||||
|
||||
waitForOrAssert(() => (resultSets = store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526)).Any(),
|
||||
@"BeatmapSet did not import to the database in allocated time.", timeout);
|
||||
|
||||
//ensure we were stored to beatmap database backing...
|
||||
Assert.IsTrue(resultSets.Count() == 1, $@"Incorrect result count found ({resultSets.Count()} but should be 1).");
|
||||
|
||||
Func<IEnumerable<BeatmapInfo>> queryBeatmaps = () => store.QueryBeatmaps(s => s.OnlineBeatmapSetID == 241526 && s.BaseDifficultyID > 0);
|
||||
Func<IEnumerable<BeatmapSetInfo>> queryBeatmapSets = () => store.QueryBeatmapSets(s => s.OnlineBeatmapSetID == 241526);
|
||||
|
||||
//if we don't re-check here, the set will be inserted but the beatmaps won't be present yet.
|
||||
waitForOrAssert(() => queryBeatmaps().Count() == 12,
|
||||
@"Beatmaps did not import to the database in allocated time", timeout);
|
||||
|
||||
waitForOrAssert(() => queryBeatmapSets().Count() == 1,
|
||||
@"BeatmapSet did not import to the database in allocated time", timeout);
|
||||
|
||||
int countBeatmapSetBeatmaps = 0;
|
||||
int countBeatmaps = 0;
|
||||
|
||||
waitForOrAssert(() =>
|
||||
(countBeatmapSetBeatmaps = queryBeatmapSets().First().Beatmaps.Count) ==
|
||||
(countBeatmaps = queryBeatmaps().Count()),
|
||||
$@"Incorrect database beatmap count post-import ({countBeatmaps} but should be {countBeatmapSetBeatmaps}).", timeout);
|
||||
|
||||
var set = queryBeatmapSets().First();
|
||||
|
||||
foreach (BeatmapInfo b in set.Beatmaps)
|
||||
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID));
|
||||
|
||||
Assert.IsTrue(set.Beatmaps.Count > 0);
|
||||
|
||||
var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
|
||||
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
|
||||
|
||||
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
|
||||
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
|
||||
|
||||
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
|
||||
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
|
||||
|
||||
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
|
||||
Assert.IsTrue(beatmap?.HitObjects.Count > 0);
|
||||
}
|
||||
|
||||
private void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)
|
||||
{
|
||||
Action waitAction = () => { while (!result()) Thread.Sleep(20); };
|
||||
Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), failureMessage);
|
||||
}
|
||||
}
|
||||
}
|
83
osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
Normal file
83
osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.IO;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.IO
|
||||
{
|
||||
[TestFixture]
|
||||
public class OszArchiveReaderTest
|
||||
{
|
||||
[Test]
|
||||
public void TestReadBeatmaps()
|
||||
{
|
||||
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
||||
{
|
||||
var reader = new OszArchiveReader(osz);
|
||||
string[] expected =
|
||||
{
|
||||
"Soleily - Renatus (Deif) [Platter].osu",
|
||||
"Soleily - Renatus (Deif) [Rain].osu",
|
||||
"Soleily - Renatus (Deif) [Salad].osu",
|
||||
"Soleily - Renatus (ExPew) [Another].osu",
|
||||
"Soleily - Renatus (ExPew) [Hyper].osu",
|
||||
"Soleily - Renatus (ExPew) [Normal].osu",
|
||||
"Soleily - Renatus (Gamu) [Hard].osu",
|
||||
"Soleily - Renatus (Gamu) [Insane].osu",
|
||||
"Soleily - Renatus (Gamu) [Normal].osu",
|
||||
"Soleily - Renatus (MMzz) [Futsuu].osu",
|
||||
"Soleily - Renatus (MMzz) [Muzukashii].osu",
|
||||
"Soleily - Renatus (MMzz) [Oni].osu"
|
||||
};
|
||||
var maps = reader.Filenames.ToArray();
|
||||
foreach (var map in expected)
|
||||
Assert.Contains(map, maps);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestReadMetadata()
|
||||
{
|
||||
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
||||
{
|
||||
var reader = new OszArchiveReader(osz);
|
||||
|
||||
BeatmapMetadata meta;
|
||||
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||
meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
|
||||
|
||||
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
||||
Assert.AreEqual("Soleily", meta.Artist);
|
||||
Assert.AreEqual("Soleily", meta.ArtistUnicode);
|
||||
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
|
||||
Assert.AreEqual("Deif", meta.Author);
|
||||
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
|
||||
Assert.AreEqual(164471, meta.PreviewTime);
|
||||
Assert.AreEqual(string.Empty, meta.Source);
|
||||
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
|
||||
Assert.AreEqual("Renatus", meta.Title);
|
||||
Assert.AreEqual("Renatus", meta.TitleUnicode);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestReadFile()
|
||||
{
|
||||
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
|
||||
{
|
||||
var reader = new OszArchiveReader(osz);
|
||||
using (var stream = new StreamReader(
|
||||
reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||
{
|
||||
Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
osu.Game.Tests/OpenTK.dll.config
Normal file
25
osu.Game.Tests/OpenTK.dll.config
Normal file
@ -0,0 +1,25 @@
|
||||
<configuration>
|
||||
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
|
||||
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
|
||||
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
|
||||
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
|
||||
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
|
||||
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
|
||||
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
|
||||
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
|
||||
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
|
||||
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
|
||||
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
|
||||
<!-- XQuartz compatibility (X11 on Mac) -->
|
||||
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
|
||||
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
|
||||
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
|
||||
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
|
||||
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
|
||||
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
|
||||
</configuration>
|
20
osu.Game.Tests/Resources/Resource.cs
Normal file
20
osu.Game.Tests/Resources/Resource.cs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace osu.Game.Tests.Resources
|
||||
{
|
||||
public static class Resource
|
||||
{
|
||||
public static Stream OpenResource(string name)
|
||||
{
|
||||
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));
|
||||
|
||||
return Assembly.GetExecutingAssembly().GetManifestResourceStream($@"osu.Game.Tests.Resources.{name}") ??
|
||||
Assembly.LoadFrom(Path.Combine(localPath, @"osu.Game.Resources.dll")).GetManifestResourceStream($@"osu.Game.Resources.{name}");
|
||||
}
|
||||
}
|
||||
}
|
1002
osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
Normal file
1002
osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
Normal file
File diff suppressed because it is too large
Load Diff
11
osu.Game.Tests/app.config
Normal file
11
osu.Game.Tests/app.config
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
103
osu.Game.Tests/osu.Game.Tests.csproj
Normal file
103
osu.Game.Tests/osu.Game.Tests.csproj
Normal file
@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{54377672-20B1-40AF-8087-5CF73BF3953A}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>osu.Game.Tests</RootNamespace>
|
||||
<AssemblyName>osu.Game.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<LangVersion>6</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="SQLite.Net">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Win32">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Generic">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="OpenTK.dll.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
|
||||
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
|
||||
<Name>osu.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
|
||||
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
|
||||
<Name>osu.Game.Rulesets.Osu</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
|
||||
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project>
|
||||
<Name>osu.Game.Rulesets.Catch</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
|
||||
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project>
|
||||
<Name>osu.Game.Rulesets.Mania</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
|
||||
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
|
||||
<Name>osu.Game.Rulesets.Taiko</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
|
||||
<Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project>
|
||||
<Name>osu.Game</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
|
||||
<Name>osu.Game.Resources</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Beatmaps\IO\OszArchiveReaderTest.cs" />
|
||||
<Compile Include="Beatmaps\IO\ImportBeatmapTest.cs" />
|
||||
<Compile Include="Resources\Resource.cs" />
|
||||
<Compile Include="Beatmaps\Formats\OsuLegacyDecoderTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Soleily - Renatus %28Gamu%29 [Insane].osu" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
11
osu.Game.Tests/packages.config
Normal file
11
osu.Game.Tests/packages.config
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
-->
|
||||
<packages>
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
|
||||
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
|
||||
</packages>
|
@ -58,6 +58,23 @@ namespace osu.Game.Beatmaps
|
||||
ComboColors = original?.ComboColors ?? ComboColors;
|
||||
HitObjects = original?.HitObjects ?? HitObjects;
|
||||
Storyboard = original?.Storyboard ?? Storyboard;
|
||||
|
||||
if (original == null && Metadata == null)
|
||||
{
|
||||
// we may have no metadata in cases we weren't sourced from the database.
|
||||
// let's fill it (and other related fields) so we don't need to null-check it in future usages.
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = @"Unknown",
|
||||
Title = @"Unknown",
|
||||
Author = @"Unknown Creator",
|
||||
},
|
||||
Version = @"Normal",
|
||||
Difficulty = new BeatmapDifficulty()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,15 +80,29 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
// Editor
|
||||
// This bookmarks stuff is necessary because DB doesn't know how to store int[]
|
||||
public string StoredBookmarks { get; set; }
|
||||
[JsonIgnore]
|
||||
public string StoredBookmarks
|
||||
{
|
||||
get { return string.Join(",", Bookmarks); }
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Bookmarks = new int[0];
|
||||
return;
|
||||
}
|
||||
|
||||
Bookmarks = value.Split(',').Select(v =>
|
||||
{
|
||||
int val;
|
||||
bool result = int.TryParse(v, out val);
|
||||
return new { result, val };
|
||||
}).Where(p => p.result).Select(p => p.val).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[Ignore]
|
||||
[JsonIgnore]
|
||||
public int[] Bookmarks
|
||||
{
|
||||
get { return StoredBookmarks.Split(',').Select(int.Parse).ToArray(); }
|
||||
set { StoredBookmarks = string.Join(",", value); }
|
||||
}
|
||||
public int[] Bookmarks { get; set; } = new int[0];
|
||||
|
||||
public double DistanceSpacing { get; set; }
|
||||
public int BeatDivisor { get; set; }
|
||||
|
@ -550,7 +550,7 @@ namespace osu.Game.Beatmaps
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => f.Filename == filename).FileInfo.StoragePath;
|
||||
private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath;
|
||||
|
||||
protected override Texture GetBackground()
|
||||
{
|
||||
@ -573,6 +573,8 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
catch { return new TrackVirtual(); }
|
||||
}
|
||||
|
||||
protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -10,6 +10,28 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public class BeatmapOnlineInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The length in milliseconds of this beatmap's song.
|
||||
/// </summary>
|
||||
public double Length { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this beatmap has a background video.
|
||||
/// </summary>
|
||||
public bool HasVideo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of circles in this beatmap.
|
||||
/// </summary>
|
||||
[JsonProperty(@"count_circles")]
|
||||
public int CircleCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of sliders in this beatmap.
|
||||
/// </summary>
|
||||
[JsonProperty(@"count_sliders")]
|
||||
public int SliderCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of plays this beatmap has.
|
||||
/// </summary>
|
||||
|
@ -1,7 +1,9 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -10,6 +12,26 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public class BeatmapSetOnlineInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The author of the beatmaps in this set.
|
||||
/// </summary>
|
||||
public User Author;
|
||||
|
||||
/// <summary>
|
||||
/// The date this beatmap set was submitted to the online listing.
|
||||
/// </summary>
|
||||
public DateTimeOffset Submitted { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this beatmap set was ranked.
|
||||
/// </summary>
|
||||
public DateTimeOffset? Ranked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this beatmap set was last updated.
|
||||
/// </summary>
|
||||
public DateTimeOffset? LastUpdated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The different sizes of cover art for this beatmap set.
|
||||
/// </summary>
|
||||
@ -22,6 +44,11 @@ namespace osu.Game.Beatmaps
|
||||
[JsonProperty(@"previewUrl")]
|
||||
public string Preview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The beats per minute of this beatmap set's song.
|
||||
/// </summary>
|
||||
public double BPM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of plays this beatmap set has.
|
||||
/// </summary>
|
||||
|
@ -31,6 +31,8 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
|
||||
public Action<BeatmapInfo> HideDifficultyRequested;
|
||||
|
||||
public Action<BeatmapInfo> EditRequested;
|
||||
|
||||
public BeatmapSetHeader Header;
|
||||
|
||||
public List<BeatmapPanel> BeatmapPanels;
|
||||
@ -87,7 +89,8 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
Alpha = 0,
|
||||
GainedSelection = panelGainedSelection,
|
||||
HideRequested = p => HideDifficultyRequested?.Invoke(p),
|
||||
StartRequested = p => { StartRequested?.Invoke(p.Beatmap); },
|
||||
StartRequested = p => StartRequested?.Invoke(p.Beatmap),
|
||||
EditRequested = p => EditRequested?.Invoke(p.Beatmap),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
}).ToList();
|
||||
|
||||
|
@ -613,7 +613,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
string line;
|
||||
while ((line = stream.ReadLine()) != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
if (line.StartsWith("//"))
|
||||
@ -679,10 +679,12 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private KeyValuePair<string, string> splitKeyVal(string line, char separator)
|
||||
{
|
||||
var split = line.Trim().Split(new[] { separator }, 2);
|
||||
|
||||
return new KeyValuePair<string, string>
|
||||
(
|
||||
line.Remove(line.IndexOf(separator)).Trim(),
|
||||
line.Substring(line.IndexOf(separator) + 1).Trim()
|
||||
split[0].Trim(),
|
||||
split.Length > 1 ? split[1].Trim() : string.Empty
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ namespace osu.Game.Beatmaps.Timing
|
||||
/// <summary>
|
||||
/// The minimum duration required for a break to have any effect.
|
||||
/// </summary>
|
||||
private const double min_break_duration = 650;
|
||||
public const double MIN_BREAK_DURATION = 650;
|
||||
|
||||
/// <summary>
|
||||
/// The break start time.
|
||||
@ -28,6 +28,6 @@ namespace osu.Game.Beatmaps.Timing
|
||||
/// <summary>
|
||||
/// Whether the break has any effect. Breaks that are too short are culled before they are added to the beatmap.
|
||||
/// </summary>
|
||||
public bool HasEffect => Duration >= min_break_duration;
|
||||
public bool HasEffect => Duration >= MIN_BREAK_DURATION;
|
||||
}
|
||||
}
|
||||
}
|
@ -43,6 +43,7 @@ namespace osu.Game.Beatmaps
|
||||
protected abstract Beatmap GetBeatmap();
|
||||
protected abstract Texture GetBackground();
|
||||
protected abstract Track GetTrack();
|
||||
protected virtual Waveform GetWaveform() => new Waveform();
|
||||
|
||||
private Beatmap beatmap;
|
||||
private readonly object beatmapLock = new object();
|
||||
@ -96,6 +97,17 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private Waveform waveform;
|
||||
private readonly object waveformLock = new object();
|
||||
public Waveform Waveform
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (waveformLock)
|
||||
return waveform ?? (waveform = GetWaveform());
|
||||
}
|
||||
}
|
||||
|
||||
public bool TrackLoaded => track != null;
|
||||
|
||||
public void TransferTo(WorkingBeatmap other)
|
||||
@ -114,6 +126,8 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
background?.Dispose();
|
||||
background = null;
|
||||
|
||||
waveform?.Dispose();
|
||||
}
|
||||
|
||||
public void DisposeTrack()
|
||||
|
@ -54,6 +54,9 @@ namespace osu.Game.Configuration
|
||||
// Graphics
|
||||
Set(OsuSetting.ShowFpsDisplay, false);
|
||||
|
||||
Set(OsuSetting.ShowStoryboard, true);
|
||||
Set(OsuSetting.CursorRotation, true);
|
||||
|
||||
Set(OsuSetting.MenuParallax, true);
|
||||
|
||||
Set(OsuSetting.SnakingInSliders, true);
|
||||
@ -87,6 +90,7 @@ namespace osu.Game.Configuration
|
||||
GameplayCursorSize,
|
||||
AutoCursorSize,
|
||||
DimLevel,
|
||||
ShowStoryboard,
|
||||
KeyOverlay,
|
||||
FloatingComments,
|
||||
PlaybackSpeed,
|
||||
@ -96,6 +100,7 @@ namespace osu.Game.Configuration
|
||||
AudioOffset,
|
||||
MenuMusic,
|
||||
MenuVoice,
|
||||
CursorRotation,
|
||||
MenuParallax,
|
||||
BeatmapDetailTab,
|
||||
Username,
|
||||
|
@ -20,13 +20,14 @@ namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
protected override Drawable CreateCursor() => new Cursor();
|
||||
|
||||
private Bindable<bool> cursorRotate;
|
||||
private bool dragging;
|
||||
|
||||
private bool startRotation;
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
if (dragging)
|
||||
if (cursorRotate && dragging)
|
||||
{
|
||||
Debug.Assert(state.Mouse.PositionMouseDown != null);
|
||||
|
||||
@ -102,6 +103,12 @@ namespace osu.Game.Graphics.Cursor
|
||||
ActiveCursor.ScaleTo(0, 500, Easing.In);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
cursorRotate = config.GetBindable<bool>(OsuSetting.CursorRotation);
|
||||
}
|
||||
|
||||
public class Cursor : Container
|
||||
{
|
||||
private Container cursorContainer;
|
||||
|
@ -1,27 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Graphics.Processing
|
||||
{
|
||||
internal class RatioAdjust : Container
|
||||
{
|
||||
public RatioAdjust()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
Vector2 parent = Parent.DrawSize;
|
||||
|
||||
Scale = new Vector2(Math.Min(parent.Y / 768f, parent.X / 1024f));
|
||||
Size = new Vector2(1 / Scale.X);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,32 +15,91 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class IconButton : OsuClickableContainer
|
||||
{
|
||||
private readonly SpriteIcon icon;
|
||||
private readonly Box hover;
|
||||
private readonly Container content;
|
||||
private const float button_size = 30;
|
||||
|
||||
private Color4? flashColour;
|
||||
/// <summary>
|
||||
/// The colour that should be flashed when the <see cref="IconButton"/> is clicked.
|
||||
/// </summary>
|
||||
public Color4 FlashColour
|
||||
{
|
||||
get { return flashColour ?? Color4.White; }
|
||||
set { flashColour = value; }
|
||||
}
|
||||
|
||||
private Color4? iconColour;
|
||||
/// <summary>
|
||||
/// The icon colour. This does not affect <see cref="IconButton.Colour"/>.
|
||||
/// </summary>
|
||||
public Color4 IconColour
|
||||
{
|
||||
get { return iconColour ?? Color4.White; }
|
||||
set
|
||||
{
|
||||
iconColour = value;
|
||||
icon.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Color4? iconHoverColour;
|
||||
/// <summary>
|
||||
/// The icon colour while the <see cref="IconButton"/> is hovered.
|
||||
/// </summary>
|
||||
public Color4 IconHoverColour
|
||||
{
|
||||
get { return iconHoverColour ?? IconColour; }
|
||||
set { iconHoverColour = value; }
|
||||
}
|
||||
|
||||
private Color4? hoverColour;
|
||||
/// <summary>
|
||||
/// The background colour of the <see cref="IconButton"/> while it is hovered.
|
||||
/// </summary>
|
||||
public Color4 HoverColour
|
||||
{
|
||||
get { return hoverColour ?? Color4.White; }
|
||||
set
|
||||
{
|
||||
hoverColour = value;
|
||||
hover.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The icon.
|
||||
/// </summary>
|
||||
public FontAwesome Icon
|
||||
{
|
||||
get { return icon.Icon; }
|
||||
set { icon.Icon = value; }
|
||||
}
|
||||
|
||||
private const float button_size = 30;
|
||||
private Color4 flashColour;
|
||||
|
||||
/// <summary>
|
||||
/// The icon scale. This does not affect <see cref="IconButton.Scale"/>.
|
||||
/// </summary>
|
||||
public Vector2 IconScale
|
||||
{
|
||||
get { return icon.Scale; }
|
||||
set { icon.Scale = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the <see cref="IconButton"/> while it is not being pressed.
|
||||
/// </summary>
|
||||
public Vector2 ButtonSize
|
||||
{
|
||||
get { return content.Size; }
|
||||
set { content.Size = value; }
|
||||
}
|
||||
|
||||
private readonly Container content;
|
||||
private readonly SpriteIcon icon;
|
||||
private readonly Box hover;
|
||||
|
||||
public IconButton()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
Anchor = Anchor.Centre;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
content = new Container
|
||||
@ -48,7 +107,6 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(button_size),
|
||||
|
||||
CornerRadius = 5,
|
||||
Masking = true,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
@ -78,8 +136,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
hover.Colour = colours.Yellow.Opacity(0.6f);
|
||||
flashColour = colours.Yellow;
|
||||
if (hoverColour == null)
|
||||
HoverColour = colours.Yellow.Opacity(0.6f);
|
||||
|
||||
if (flashColour == null)
|
||||
FlashColour = colours.Yellow;
|
||||
|
||||
Enabled.ValueChanged += enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint);
|
||||
}
|
||||
@ -87,18 +148,20 @@ namespace osu.Game.Graphics.UserInterface
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
hover.FadeIn(500, Easing.OutQuint);
|
||||
icon.FadeColour(IconHoverColour, 500, Easing.OutQuint);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
hover.FadeOut(500, Easing.OutQuint);
|
||||
icon.FadeColour(IconColour, 500, Easing.OutQuint);
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
hover.FlashColour(flashColour, 800, Easing.OutQuint);
|
||||
hover.FlashColour(FlashColour, 800, Easing.OutQuint);
|
||||
return base.OnClick(state);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
public class OsuTabItem : TabItem<T>, IHasAccentColour
|
||||
{
|
||||
protected readonly SpriteText Text;
|
||||
private readonly Box box;
|
||||
protected readonly Box Bar;
|
||||
|
||||
private Color4 accentColour;
|
||||
public Color4 AccentColour
|
||||
@ -77,13 +77,13 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
private void fadeActive()
|
||||
{
|
||||
box.FadeIn(transition_length, Easing.OutQuint);
|
||||
Bar.FadeIn(transition_length, Easing.OutQuint);
|
||||
Text.FadeColour(Color4.White, transition_length, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void fadeInactive()
|
||||
{
|
||||
box.FadeOut(transition_length, Easing.OutQuint);
|
||||
Bar.FadeOut(transition_length, Easing.OutQuint);
|
||||
Text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-Bold", // Font should only turn bold when active?
|
||||
},
|
||||
box = new Box
|
||||
Bar = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 1,
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.IO.Legacy
|
||||
int len = ReadInt32();
|
||||
if (len > 0) return ReadBytes(len);
|
||||
if (len < 0) return null;
|
||||
return new byte[0];
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
/// <summary> Reads a char array from the buffer, handling nulls and the array length. </summary>
|
||||
@ -58,7 +58,7 @@ namespace osu.Game.IO.Legacy
|
||||
int len = ReadInt32();
|
||||
if (len > 0) return ReadChars(len);
|
||||
if (len < 0) return null;
|
||||
return new char[0];
|
||||
return Array.Empty<char>();
|
||||
}
|
||||
|
||||
/// <summary> Reads a DateTime from the buffer. </summary>
|
||||
|
@ -101,18 +101,16 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
break;
|
||||
case APIState.Offline:
|
||||
case APIState.Connecting:
|
||||
//work to restore a connection...
|
||||
if (!HasLogin)
|
||||
{
|
||||
//OsuGame.Scheduler.Add(() => { OsuGame.ShowLogin(); });
|
||||
|
||||
State = APIState.Offline;
|
||||
Thread.Sleep(500);
|
||||
Thread.Sleep(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (State < APIState.Connecting)
|
||||
State = APIState.Connecting;
|
||||
State = APIState.Connecting;
|
||||
|
||||
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password))
|
||||
{
|
||||
@ -125,7 +123,8 @@ namespace osu.Game.Online.API
|
||||
|
||||
|
||||
var userReq = new GetUserRequest();
|
||||
userReq.Success += u => {
|
||||
userReq.Success += u =>
|
||||
{
|
||||
LocalUser.Value = u;
|
||||
//we're connected!
|
||||
State = APIState.Online;
|
||||
@ -133,16 +132,14 @@ namespace osu.Game.Online.API
|
||||
};
|
||||
|
||||
if (!handleRequest(userReq))
|
||||
{
|
||||
State = APIState.Failing;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//hard bail if we can't get a valid access token.
|
||||
if (authentication.RequestAccessToken() == null)
|
||||
{
|
||||
Logout(false);
|
||||
State = APIState.Offline;
|
||||
continue;
|
||||
}
|
||||
@ -162,20 +159,12 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
}
|
||||
|
||||
private void clearCredentials()
|
||||
{
|
||||
Username = null;
|
||||
Password = null;
|
||||
}
|
||||
|
||||
public void Login(string username, string password)
|
||||
{
|
||||
Debug.Assert(State == APIState.Offline);
|
||||
|
||||
Username = username;
|
||||
Password = password;
|
||||
|
||||
State = APIState.Connecting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -204,7 +193,7 @@ namespace osu.Game.Online.API
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
State = APIState.Offline;
|
||||
Logout(false);
|
||||
return true;
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
failureCount++;
|
||||
@ -215,6 +204,7 @@ namespace osu.Game.Online.API
|
||||
return false;
|
||||
|
||||
State = APIState.Failing;
|
||||
flushQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -235,33 +225,21 @@ namespace osu.Game.Online.API
|
||||
public APIState State
|
||||
{
|
||||
get { return state; }
|
||||
set
|
||||
private set
|
||||
{
|
||||
APIState oldState = state;
|
||||
APIState newState = value;
|
||||
|
||||
state = value;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Failing:
|
||||
case APIState.Offline:
|
||||
flushQueue();
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldState != newState)
|
||||
{
|
||||
//OsuGame.Scheduler.Add(delegate
|
||||
log.Add($@"We just went {newState}!");
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
//NotificationOverlay.ShowMessage($@"We just went {newState}!", newState == APIState.Online ? Color4.YellowGreen : Color4.OrangeRed, 5000);
|
||||
log.Add($@"We just went {newState}!");
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
components.ForEach(c => c.APIStateChanged(this, newState));
|
||||
OnStateChange?.Invoke(oldState, newState);
|
||||
});
|
||||
}
|
||||
components.ForEach(c => c.APIStateChanged(this, newState));
|
||||
OnStateChange?.Invoke(oldState, newState);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -292,11 +270,12 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
}
|
||||
|
||||
public void Logout()
|
||||
public void Logout(bool clearUsername = true)
|
||||
{
|
||||
clearCredentials();
|
||||
flushQueue();
|
||||
if (clearUsername) Username = null;
|
||||
Password = null;
|
||||
authentication.Clear();
|
||||
State = APIState.Offline;
|
||||
LocalUser.Value = createGuestUser();
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
@ -70,13 +69,11 @@ namespace osu.Game.Online.API
|
||||
|
||||
protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}";
|
||||
|
||||
private double remainingTime => Math.Max(0, Timeout - (DateTime.Now.TotalMilliseconds() - (startTime ?? 0)));
|
||||
private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds);
|
||||
|
||||
public bool ExceededTimeout => remainingTime == 0;
|
||||
|
||||
private double? startTime;
|
||||
|
||||
public double StartTime => startTime ?? -1;
|
||||
private DateTimeOffset? startTime;
|
||||
|
||||
protected APIAccess API;
|
||||
protected WebRequest WebRequest;
|
||||
@ -96,7 +93,7 @@ namespace osu.Game.Online.API
|
||||
return;
|
||||
|
||||
if (startTime == null)
|
||||
startTime = DateTime.Now.TotalMilliseconds();
|
||||
startTime = DateTimeOffset.UtcNow;
|
||||
|
||||
if (remainingTime <= 0)
|
||||
throw new TimeoutException(@"API request timeout hit");
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Extensions;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
@ -22,12 +21,12 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
get
|
||||
{
|
||||
return AccessTokenExpiry - DateTime.Now.ToUnixTimestamp();
|
||||
return AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
AccessTokenExpiry = DateTime.Now.AddSeconds(value).ToUnixTimestamp();
|
||||
AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
private readonly BeatmapInfo beatmap;
|
||||
|
||||
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={beatmap.Path}";
|
||||
private string lookupString => beatmap.OnlineBeatmapID > 0 ? beatmap.OnlineBeatmapID.ToString() : $@"lookup?checksum={beatmap.Hash}&filename={System.Uri.EscapeUriString(beatmap.Path)}";
|
||||
|
||||
public GetBeatmapDetailsRequest(BeatmapInfo beatmap)
|
||||
{
|
||||
|
@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
@ -49,6 +50,12 @@ namespace osu.Game.Online.API.Requests
|
||||
[JsonProperty(@"id")]
|
||||
private int onlineId { get; set; }
|
||||
|
||||
[JsonProperty(@"creator")]
|
||||
private string creatorUsername { get; set; }
|
||||
|
||||
[JsonProperty(@"user_id")]
|
||||
private long creatorId = 1;
|
||||
|
||||
[JsonProperty(@"beatmaps")]
|
||||
private IEnumerable<GetBeatmapSetsBeatmapResponse> beatmaps { get; set; }
|
||||
|
||||
@ -60,6 +67,11 @@ namespace osu.Game.Online.API.Requests
|
||||
Metadata = this,
|
||||
OnlineInfo = new BeatmapSetOnlineInfo
|
||||
{
|
||||
Author = new User
|
||||
{
|
||||
Id = creatorId,
|
||||
Username = creatorUsername,
|
||||
},
|
||||
Covers = covers,
|
||||
Preview = preview,
|
||||
PlayCount = playCount,
|
||||
|
@ -96,7 +96,7 @@ namespace osu.Game.Online.API.Requests
|
||||
}
|
||||
|
||||
[JsonProperty(@"statistics")]
|
||||
private Dictionary<string, dynamic> jsonStats
|
||||
private Dictionary<string, object> jsonStats
|
||||
{
|
||||
set
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Online.API.Requests
|
||||
req.Method = HttpMethod.POST;
|
||||
req.AddParameter(@"target_type", message.TargetType.GetDescription());
|
||||
req.AddParameter(@"target_id", message.TargetId.ToString());
|
||||
req.AddParameter(@"is_action", message.IsAction.ToString().ToLower());
|
||||
req.AddParameter(@"message", message.Content);
|
||||
|
||||
return req;
|
||||
@ -30,4 +31,4 @@ namespace osu.Game.Online.API.Requests
|
||||
|
||||
protected override string Target => @"chat/messages";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,13 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class ErrorMessage : Message
|
||||
public class ErrorMessage : InfoMessage
|
||||
{
|
||||
private static int errorId = -1;
|
||||
|
||||
public ErrorMessage(string message) : base(errorId--)
|
||||
public ErrorMessage(string message) : base(message)
|
||||
{
|
||||
Timestamp = DateTimeOffset.Now;
|
||||
Content = message;
|
||||
|
||||
Sender = new User
|
||||
{
|
||||
Username = @"system",
|
||||
Colour = @"ff0000",
|
||||
};
|
||||
Sender.Colour = @"ff0000";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
osu.Game/Online/Chat/InfoMessage.cs
Normal file
25
osu.Game/Online/Chat/InfoMessage.cs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class InfoMessage : Message
|
||||
{
|
||||
private static int infoID = -1;
|
||||
|
||||
public InfoMessage(string message) : base(infoID--)
|
||||
{
|
||||
Timestamp = DateTimeOffset.Now;
|
||||
Content = message;
|
||||
|
||||
Sender = new User
|
||||
{
|
||||
Username = @"system",
|
||||
Colour = @"0000ff",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,9 @@ namespace osu.Game.Online.Chat
|
||||
[JsonProperty(@"target_id")]
|
||||
public int TargetId;
|
||||
|
||||
[JsonProperty(@"is_action")]
|
||||
public bool IsAction;
|
||||
|
||||
[JsonProperty(@"timestamp")]
|
||||
public DateTimeOffset Timestamp;
|
||||
|
||||
|
@ -47,6 +47,8 @@ namespace osu.Game
|
||||
|
||||
private UserProfileOverlay userProfile;
|
||||
|
||||
private BeatmapSetOverlay beatmapSetOverlay;
|
||||
|
||||
public virtual Storage GetStorageForStableInstall() => null;
|
||||
|
||||
private Intro intro
|
||||
@ -186,10 +188,11 @@ namespace osu.Game
|
||||
GetToolbarHeight = () => ToolbarOffset,
|
||||
Depth = -1
|
||||
}, overlayContent.Add);
|
||||
LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add);
|
||||
LoadComponentAsync(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -2 }, mainContent.Add);
|
||||
LoadComponentAsync(userProfile = new UserProfileOverlay { Depth = -3 }, mainContent.Add);
|
||||
LoadComponentAsync(musicController = new MusicController
|
||||
{
|
||||
Depth = -3,
|
||||
Depth = -4,
|
||||
Position = new Vector2(0, Toolbar.HEIGHT),
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
@ -197,7 +200,7 @@ namespace osu.Game
|
||||
|
||||
LoadComponentAsync(notificationOverlay = new NotificationOverlay
|
||||
{
|
||||
Depth = -3,
|
||||
Depth = -4,
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
}, overlayContent.Add);
|
||||
@ -223,6 +226,7 @@ namespace osu.Game
|
||||
dependencies.Cache(chat);
|
||||
dependencies.Cache(userProfile);
|
||||
dependencies.Cache(musicController);
|
||||
dependencies.Cache(beatmapSetOverlay);
|
||||
dependencies.Cache(notificationOverlay);
|
||||
dependencies.Cache(dialogOverlay);
|
||||
|
||||
|
@ -15,7 +15,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Processing;
|
||||
using osu.Game.Online.API;
|
||||
using SQLite.Net;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
@ -82,6 +81,13 @@ namespace osu.Game
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
|
||||
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
private SQLiteConnection createConnection()
|
||||
{
|
||||
var conn = Host.Storage.GetDatabase(@"client");
|
||||
conn.BusyTimeout = new TimeSpan(TimeSpan.TicksPerSecond * 10);
|
||||
return conn;
|
||||
}
|
||||
|
||||
private SQLiteConnection connection;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -90,8 +96,7 @@ namespace osu.Game
|
||||
dependencies.Cache(this);
|
||||
dependencies.Cache(LocalConfig);
|
||||
|
||||
connection = Host.Storage.GetDatabase(@"client");
|
||||
|
||||
connection = createConnection();
|
||||
connection.CreateTable<StoreVersion>();
|
||||
|
||||
dependencies.Cache(API = new APIAccess
|
||||
@ -180,7 +185,7 @@ namespace osu.Game
|
||||
|
||||
GlobalKeyBindingInputManager globalBinding;
|
||||
|
||||
base.Content.Add(new RatioAdjust
|
||||
base.Content.Add(new DrawSizePreservingFillContainer
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
|
131
osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
Normal file
131
osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Users;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class AuthorInfo : Container
|
||||
{
|
||||
private const float height = 50;
|
||||
|
||||
private readonly UpdateableAvatar avatar;
|
||||
private readonly ClickableArea clickableArea;
|
||||
private readonly FillFlowContainer fields;
|
||||
|
||||
private UserProfileOverlay profile;
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return beatmapSet; }
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
var i = BeatmapSet.OnlineInfo;
|
||||
|
||||
avatar.User = i.Author;
|
||||
clickableArea.Action = () => profile?.ShowUser(avatar.User);
|
||||
|
||||
fields.Children = new Drawable[]
|
||||
{
|
||||
new Field("made by", i.Author.Username, @"Exo2.0-RegularItalic"),
|
||||
new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
},
|
||||
};
|
||||
|
||||
if (i.Ranked.HasValue)
|
||||
{
|
||||
fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
|
||||
}
|
||||
else if (i.LastUpdated.HasValue)
|
||||
{
|
||||
fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AuthorInfo()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
clickableArea = new ClickableArea
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
CornerRadius = 3,
|
||||
Masking = true,
|
||||
Child = avatar = new UpdateableAvatar
|
||||
{
|
||||
Size = new Vector2(height),
|
||||
},
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 3,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
},
|
||||
},
|
||||
fields = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Padding = new MarginPadding { Left = height + 5 },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(UserProfileOverlay profile)
|
||||
{
|
||||
this.profile = profile;
|
||||
clickableArea.Action = () => profile?.ShowUser(avatar.User);
|
||||
}
|
||||
|
||||
private class Field : FillFlowContainer
|
||||
{
|
||||
public Field(string first, string second, string secondFont)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Direction = FillDirection.Horizontal;
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = $"{first} ",
|
||||
TextSize = 13,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = second,
|
||||
TextSize = 13,
|
||||
Font = secondFont,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class ClickableArea : OsuClickableContainer, IHasTooltip
|
||||
{
|
||||
public string TooltipText => @"View Profile";
|
||||
}
|
||||
}
|
||||
}
|
130
osu.Game/Overlays/BeatmapSet/BasicStats.cs
Normal file
130
osu.Game/Overlays/BeatmapSet/BasicStats.cs
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class BasicStats : Container
|
||||
{
|
||||
private readonly Statistic length, bpm, circleCount, sliderCount;
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return beatmapSet; }
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##");
|
||||
}
|
||||
}
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get { return beatmap; }
|
||||
set
|
||||
{
|
||||
if (value == beatmap) return;
|
||||
beatmap = value;
|
||||
|
||||
length.Value = TimeSpan.FromMilliseconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
|
||||
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString("N0");
|
||||
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString("N0");
|
||||
}
|
||||
}
|
||||
|
||||
public BasicStats()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new[]
|
||||
{
|
||||
length = new Statistic(FontAwesome.fa_clock_o, "Length") { Width = 0.25f },
|
||||
bpm = new Statistic(FontAwesome.fa_circle, "BPM") { Width = 0.25f },
|
||||
circleCount = new Statistic(FontAwesome.fa_circle_o, "Circle Count") { Width = 0.25f },
|
||||
sliderCount = new Statistic(FontAwesome.fa_circle, "Slider Count") { Width = 0.25f },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private class Statistic : Container, IHasTooltip
|
||||
{
|
||||
private readonly string name;
|
||||
private readonly OsuSpriteText value;
|
||||
|
||||
public string TooltipText => name;
|
||||
public string Value
|
||||
{
|
||||
get { return value.Text; }
|
||||
set { this.value.Text = value; }
|
||||
}
|
||||
|
||||
public Statistic(FontAwesome icon, string name)
|
||||
{
|
||||
this.name = name;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.fa_square,
|
||||
Size = new Vector2(13),
|
||||
Rotation = 45,
|
||||
Colour = OsuColour.FromHex(@"441288"),
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = icon,
|
||||
Size = new Vector2(13),
|
||||
Colour = OsuColour.FromHex(@"f7dd55"),
|
||||
Scale = new Vector2(0.8f),
|
||||
},
|
||||
value = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
TextSize = 13,
|
||||
Font = @"Exo2.0-Bold",
|
||||
Margin = new MarginPadding { Left = 10 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour)
|
||||
{
|
||||
value.Colour = colour.Yellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
312
osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
Normal file
312
osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
Normal file
@ -0,0 +1,312 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class BeatmapPicker : Container
|
||||
{
|
||||
private const float tile_icon_padding = 7;
|
||||
private const float tile_spacing = 2;
|
||||
|
||||
private readonly DifficultiesContainer difficulties;
|
||||
private readonly OsuSpriteText version, starRating;
|
||||
private readonly Statistic plays, favourites;
|
||||
|
||||
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return beatmapSet; }
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
Beatmap.Value = BeatmapSet.Beatmaps.First();
|
||||
plays.Value = BeatmapSet.OnlineInfo.PlayCount;
|
||||
favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount;
|
||||
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Select(b => new DifficultySelectorButton(b)
|
||||
{
|
||||
State = DifficultySelectorState.NotSelected,
|
||||
OnHovered = beatmap =>
|
||||
{
|
||||
showBeatmap(beatmap);
|
||||
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
|
||||
starRating.FadeIn(100);
|
||||
},
|
||||
OnClicked = beatmap =>
|
||||
{
|
||||
Beatmap.Value = beatmap;
|
||||
},
|
||||
});
|
||||
|
||||
updateDifficultyButtons();
|
||||
}
|
||||
}
|
||||
|
||||
public BeatmapPicker()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
difficulties = new DifficultiesContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Left = -(tile_icon_padding + tile_spacing / 2) },
|
||||
OnLostHover = () =>
|
||||
{
|
||||
showBeatmap(Beatmap.Value);
|
||||
starRating.FadeOut(100);
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Spacing = new Vector2(5f),
|
||||
Children = new[]
|
||||
{
|
||||
version = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
TextSize = 20,
|
||||
Font = @"Exo2.0-Bold",
|
||||
},
|
||||
starRating = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
TextSize = 13,
|
||||
Font = @"Exo2.0-Bold",
|
||||
Text = "Star Difficulty",
|
||||
Alpha = 0,
|
||||
Margin = new MarginPadding { Bottom = 1 },
|
||||
},
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(10f),
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
Children = new[]
|
||||
{
|
||||
plays = new Statistic(FontAwesome.fa_play_circle),
|
||||
favourites = new Statistic(FontAwesome.fa_heart),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Beatmap.ValueChanged += b =>
|
||||
{
|
||||
showBeatmap(b);
|
||||
updateDifficultyButtons();
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
starRating.Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// done here so everything can bind in intialization and get the first trigger
|
||||
Beatmap.TriggerChange();
|
||||
}
|
||||
|
||||
private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version;
|
||||
|
||||
private void updateDifficultyButtons()
|
||||
{
|
||||
difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected);
|
||||
}
|
||||
|
||||
private class DifficultiesContainer : FillFlowContainer<DifficultySelectorButton>
|
||||
{
|
||||
public Action OnLostHover;
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
base.OnHoverLost(state);
|
||||
OnLostHover?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private class DifficultySelectorButton : OsuClickableContainer, IStateful<DifficultySelectorState>
|
||||
{
|
||||
private const float transition_duration = 100;
|
||||
private const float size = 52;
|
||||
|
||||
private readonly Container bg;
|
||||
private readonly DifficultyIcon icon;
|
||||
|
||||
public readonly BeatmapInfo Beatmap;
|
||||
|
||||
public Action<BeatmapInfo> OnHovered;
|
||||
public Action<BeatmapInfo> OnClicked;
|
||||
public event Action<DifficultySelectorState> StateChanged;
|
||||
|
||||
private DifficultySelectorState state;
|
||||
public DifficultySelectorState State
|
||||
{
|
||||
get { return state; }
|
||||
set
|
||||
{
|
||||
if (value == state) return;
|
||||
state = value;
|
||||
|
||||
StateChanged?.Invoke(State);
|
||||
if (value == DifficultySelectorState.Selected)
|
||||
fadeIn();
|
||||
else
|
||||
fadeOut();
|
||||
}
|
||||
}
|
||||
|
||||
public DifficultySelectorButton(BeatmapInfo beatmap)
|
||||
{
|
||||
Beatmap = beatmap;
|
||||
Size = new Vector2(size);
|
||||
Margin = new MarginPadding { Horizontal = tile_spacing / 2 };
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
bg = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
CornerRadius = 4,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
},
|
||||
icon = new DifficultyIcon(beatmap)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(size - tile_icon_padding * 2),
|
||||
Margin = new MarginPadding { Bottom = 1 },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
fadeIn();
|
||||
OnHovered?.Invoke(Beatmap);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
if (State == DifficultySelectorState.NotSelected)
|
||||
fadeOut();
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
OnClicked?.Invoke(Beatmap);
|
||||
return base.OnClick(state);
|
||||
}
|
||||
|
||||
private void fadeIn()
|
||||
{
|
||||
bg.FadeIn(transition_duration);
|
||||
icon.FadeIn(transition_duration);
|
||||
}
|
||||
|
||||
private void fadeOut()
|
||||
{
|
||||
bg.FadeOut();
|
||||
icon.FadeTo(0.7f, transition_duration);
|
||||
}
|
||||
}
|
||||
|
||||
private class Statistic : FillFlowContainer
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
|
||||
private int value;
|
||||
public int Value
|
||||
{
|
||||
get { return value; }
|
||||
set
|
||||
{
|
||||
this.value = value;
|
||||
text.Text = Value.ToString(@"N0");
|
||||
}
|
||||
}
|
||||
|
||||
public Statistic(FontAwesome icon)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Direction = FillDirection.Horizontal;
|
||||
Spacing = new Vector2(2f);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Icon = icon,
|
||||
Shadow = true,
|
||||
Size = new Vector2(13),
|
||||
},
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = @"Exo2.0-SemiBoldItalic",
|
||||
TextSize = 14,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private enum DifficultySelectorState
|
||||
{
|
||||
Selected,
|
||||
NotSelected,
|
||||
}
|
||||
}
|
||||
}
|
118
osu.Game/Overlays/BeatmapSet/Details.cs
Normal file
118
osu.Game/Overlays/BeatmapSet/Details.cs
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Select.Details;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class Details : FillFlowContainer
|
||||
{
|
||||
private readonly PreviewButton preview;
|
||||
private readonly BasicStats basic;
|
||||
private readonly AdvancedStats advanced;
|
||||
private readonly UserRatings ratings;
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return beatmapSet; }
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
basic.BeatmapSet = preview.BeatmapSet = BeatmapSet;
|
||||
}
|
||||
}
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get { return beatmap; }
|
||||
set
|
||||
{
|
||||
if (value == beatmap) return;
|
||||
beatmap = value;
|
||||
|
||||
basic.Beatmap = advanced.Beatmap = Beatmap;
|
||||
ratings.Metrics = Beatmap.Metrics;
|
||||
}
|
||||
}
|
||||
|
||||
public Details()
|
||||
{
|
||||
Width = BeatmapSetOverlay.RIGHT_WIDTH;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Spacing = new Vector2(1f);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
preview = new PreviewButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
new DetailBox
|
||||
{
|
||||
Child = basic = new BasicStats
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Vertical = 10 },
|
||||
},
|
||||
},
|
||||
new DetailBox
|
||||
{
|
||||
Child = advanced = new AdvancedStats
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Vertical = 7.5f },
|
||||
},
|
||||
},
|
||||
new DetailBox
|
||||
{
|
||||
Child = ratings = new UserRatings
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 95,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private class DetailBox : Container
|
||||
{
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
public DetailBox()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = 15 },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
osu.Game/Overlays/BeatmapSet/DownloadButton.cs
Normal file
59
osu.Game/Overlays/BeatmapSet/DownloadButton.cs
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class DownloadButton : HeaderButton
|
||||
{
|
||||
public DownloadButton(string title, string subtitle)
|
||||
{
|
||||
Width = 120;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
|
||||
Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Horizontal = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = title,
|
||||
TextSize = 13,
|
||||
Font = @"Exo2.0-Bold",
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = subtitle,
|
||||
TextSize = 11,
|
||||
Font = @"Exo2.0-Bold",
|
||||
},
|
||||
},
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Icon = FontAwesome.fa_download,
|
||||
Size = new Vector2(16),
|
||||
Margin = new MarginPadding { Right = 5 },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
80
osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
Normal file
80
osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class FavouriteButton : HeaderButton
|
||||
{
|
||||
public readonly Bindable<bool> Favourited = new Bindable<bool>();
|
||||
|
||||
public FavouriteButton()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
|
||||
Container pink;
|
||||
SpriteIcon icon;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
pink = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.FromHex(@"9f015f"),
|
||||
},
|
||||
new Triangles
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColourLight = OsuColour.FromHex(@"cb2187"),
|
||||
ColourDark = OsuColour.FromHex(@"9f015f"),
|
||||
TriangleScale = 1.5f,
|
||||
},
|
||||
},
|
||||
},
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.fa_heart_o,
|
||||
Size = new Vector2(18),
|
||||
Shadow = false,
|
||||
},
|
||||
};
|
||||
|
||||
Favourited.ValueChanged += value =>
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
pink.FadeIn(200);
|
||||
icon.Icon = FontAwesome.fa_heart;
|
||||
}
|
||||
else
|
||||
{
|
||||
pink.FadeOut(200);
|
||||
icon.Icon = FontAwesome.fa_heart_o;
|
||||
}
|
||||
};
|
||||
|
||||
Action = () => Favourited.Value = !Favourited.Value;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
Width = DrawHeight;
|
||||
}
|
||||
}
|
||||
}
|
228
osu.Game/Overlays/BeatmapSet/Header.cs
Normal file
228
osu.Game/Overlays/BeatmapSet/Header.cs
Normal file
@ -0,0 +1,228 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class Header : Container
|
||||
{
|
||||
private const float transition_duration = 250;
|
||||
private const float tabs_height = 50;
|
||||
private const float buttons_height = 45;
|
||||
private const float buttons_spacing = 5;
|
||||
|
||||
private readonly Box tabsBg;
|
||||
private readonly Container coverContainer;
|
||||
private readonly OsuSpriteText title, artist;
|
||||
private readonly AuthorInfo author;
|
||||
private readonly Details details;
|
||||
|
||||
private DelayedLoadWrapper cover;
|
||||
|
||||
public readonly BeatmapPicker Picker;
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return beatmapSet; }
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
Picker.BeatmapSet = author.BeatmapSet = details.BeatmapSet = BeatmapSet;
|
||||
title.Text = BeatmapSet.Metadata.Title;
|
||||
artist.Text = BeatmapSet.Metadata.Artist;
|
||||
|
||||
cover?.FadeOut(400, Easing.Out);
|
||||
coverContainer.Add(cover = new DelayedLoadWrapper(new BeatmapSetCover(BeatmapSet)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
OnLoadComplete = d =>
|
||||
{
|
||||
d.FadeInFromZero(400, Easing.Out);
|
||||
},
|
||||
})
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
TimeBeforeLoad = 300
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Header()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 400;
|
||||
Masking = true;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 3,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
};
|
||||
|
||||
Container noVideoButtons;
|
||||
FillFlowContainer videoButtons;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = tabs_height,
|
||||
Children = new[]
|
||||
{
|
||||
tabsBg = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = tabs_height },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
coverContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.3f), Color4.Black.Opacity(0.8f)),
|
||||
},
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 20, Bottom = 30, Horizontal = BeatmapSetOverlay.X_PADDING },
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 113,
|
||||
Child = Picker = new BeatmapPicker(),
|
||||
},
|
||||
title = new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
TextSize = 37,
|
||||
},
|
||||
artist = new OsuSpriteText
|
||||
{
|
||||
Font = @"Exo2.0-SemiBoldItalic",
|
||||
TextSize = 25,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Child = author = new AuthorInfo(),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = buttons_height,
|
||||
Margin = new MarginPadding { Top = 10 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FavouriteButton(),
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = buttons_height + buttons_spacing },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
noVideoButtons = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0f,
|
||||
Child = new DownloadButton("Download", @""),
|
||||
},
|
||||
videoButtons = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(buttons_spacing),
|
||||
Alpha = 0f,
|
||||
Children = new[]
|
||||
{
|
||||
new DownloadButton("Download", "with Video"),
|
||||
new DownloadButton("Download", "without Video"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
details = new Details
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding { Right = BeatmapSetOverlay.X_PADDING },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Picker.Beatmap.ValueChanged += b =>
|
||||
{
|
||||
details.Beatmap = b;
|
||||
|
||||
if (b.OnlineInfo.HasVideo)
|
||||
{
|
||||
noVideoButtons.FadeOut(transition_duration);
|
||||
videoButtons.FadeIn(transition_duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
noVideoButtons.FadeIn(transition_duration);
|
||||
videoButtons.FadeOut(transition_duration);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
tabsBg.Colour = colours.Gray3;
|
||||
}
|
||||
}
|
||||
}
|
45
osu.Game/Overlays/BeatmapSet/HeaderButton.cs
Normal file
45
osu.Game/Overlays/BeatmapSet/HeaderButton.cs
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class HeaderButton : OsuClickableContainer
|
||||
{
|
||||
private readonly Container content;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
public HeaderButton()
|
||||
{
|
||||
CornerRadius = 3;
|
||||
Masking = true;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.FromHex(@"094c5f"),
|
||||
},
|
||||
new Triangles
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColourLight = OsuColour.FromHex(@"0f7c9b"),
|
||||
ColourDark = OsuColour.FromHex(@"094c5f"),
|
||||
TriangleScale = 1.5f,
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
196
osu.Game/Overlays/BeatmapSet/Info.cs
Normal file
196
osu.Game/Overlays/BeatmapSet/Info.cs
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class Info : Container
|
||||
{
|
||||
private const float transition_duration = 250;
|
||||
private const float metadata_width = 225;
|
||||
private const float spacing = 20;
|
||||
|
||||
private readonly MetadataSection description, source, tags;
|
||||
private readonly Box successRateBackground;
|
||||
private readonly SuccessRate successRate;
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return beatmapSet; }
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
source.Text = BeatmapSet.Metadata.Source;
|
||||
tags.Text = BeatmapSet.Metadata.Tags;
|
||||
}
|
||||
}
|
||||
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get { return successRate.Beatmap; }
|
||||
set { successRate.Beatmap = value; }
|
||||
}
|
||||
|
||||
public Info()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 220;
|
||||
Masking = true;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 3,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 15, Horizontal = BeatmapSetOverlay.X_PADDING },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Right = metadata_width + BeatmapSetOverlay.RIGHT_WIDTH + spacing * 2 },
|
||||
Child = new ScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = description = new MetadataSection("Description"),
|
||||
},
|
||||
},
|
||||
new ScrollContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = metadata_width,
|
||||
ScrollbarVisible = false,
|
||||
Padding = new MarginPadding { Horizontal = 10 },
|
||||
Margin = new MarginPadding { Right = BeatmapSetOverlay.RIGHT_WIDTH + spacing },
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
LayoutDuration = transition_duration,
|
||||
Children = new[]
|
||||
{
|
||||
source = new MetadataSection("Source"),
|
||||
tags = new MetadataSection("Tags"),
|
||||
},
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = BeatmapSetOverlay.RIGHT_WIDTH,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
successRateBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
successRate = new SuccessRate
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = 20, Horizontal = 15 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
successRateBackground.Colour = colours.GrayE;
|
||||
source.TextColour = description.TextColour = colours.Gray5;
|
||||
tags.TextColour = colours.BlueDark;
|
||||
}
|
||||
|
||||
private class MetadataSection : FillFlowContainer
|
||||
{
|
||||
private readonly OsuSpriteText header;
|
||||
private readonly TextFlowContainer textFlow;
|
||||
|
||||
public string Text
|
||||
{
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
this.FadeOut(transition_duration);
|
||||
return;
|
||||
}
|
||||
|
||||
this.FadeIn(transition_duration);
|
||||
textFlow.Clear();
|
||||
textFlow.AddText(value, s => s.TextSize = 14);
|
||||
}
|
||||
}
|
||||
|
||||
public Color4 TextColour
|
||||
{
|
||||
get { return textFlow.Colour; }
|
||||
set { textFlow.Colour = value; }
|
||||
}
|
||||
|
||||
public MetadataSection(string title)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Spacing = new Vector2(5f);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
header = new OsuSpriteText
|
||||
{
|
||||
Text = title,
|
||||
Font = @"Exo2.0-Bold",
|
||||
TextSize = 14,
|
||||
Shadow = false,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
},
|
||||
textFlow = new TextFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
header.Colour = colours.Gray5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
107
osu.Game/Overlays/BeatmapSet/PreviewButton.cs
Normal file
107
osu.Game/Overlays/BeatmapSet/PreviewButton.cs
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Framework.Configuration;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class PreviewButton : OsuClickableContainer
|
||||
{
|
||||
private const float transition_duration = 500;
|
||||
|
||||
private readonly Box bg, progress;
|
||||
private readonly PlayButton playButton;
|
||||
|
||||
private Track preview => playButton.Preview;
|
||||
private Bindable<bool> playing => playButton.Playing;
|
||||
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return playButton.BeatmapSet; }
|
||||
set { playButton.BeatmapSet = value; }
|
||||
}
|
||||
|
||||
public PreviewButton()
|
||||
{
|
||||
Height = 42;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
bg = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 3,
|
||||
Child = progress = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0f,
|
||||
Alpha = 0f,
|
||||
},
|
||||
},
|
||||
playButton = new PlayButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(18),
|
||||
},
|
||||
};
|
||||
|
||||
Action = () => playing.Value = !playing.Value;
|
||||
playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
progress.Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (playing.Value && preview != null)
|
||||
{
|
||||
progress.Width = (float)(preview.CurrentTime / preview.Length);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
playing.Value = false;
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
bg.FadeColour(Color4.Black.Opacity(0.5f), 100);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
bg.FadeColour(Color4.Black.Opacity(0.25f), 100);
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
}
|
||||
}
|
113
osu.Game/Overlays/BeatmapSet/SuccessRate.cs
Normal file
113
osu.Game/Overlays/BeatmapSet/SuccessRate.cs
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Select.Details;
|
||||
|
||||
namespace osu.Game.Overlays.BeatmapSet
|
||||
{
|
||||
public class SuccessRate : Container
|
||||
{
|
||||
private readonly FillFlowContainer header;
|
||||
private readonly OsuSpriteText successRateLabel, successPercent, graphLabel;
|
||||
private readonly Bar successRate;
|
||||
private readonly Container percentContainer;
|
||||
private readonly FailRetryGraph graph;
|
||||
|
||||
private BeatmapInfo beatmap;
|
||||
public BeatmapInfo Beatmap
|
||||
{
|
||||
get { return beatmap; }
|
||||
set
|
||||
{
|
||||
if (value == beatmap) return;
|
||||
beatmap = value;
|
||||
|
||||
var rate = (float)beatmap.OnlineInfo.PassCount / beatmap.OnlineInfo.PlayCount;
|
||||
successPercent.Text = $"{Math.Round(rate * 100)}%";
|
||||
successRate.Length = rate;
|
||||
percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
|
||||
|
||||
graph.Metrics = Beatmap.Metrics;
|
||||
}
|
||||
}
|
||||
|
||||
public SuccessRate()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
header = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
successRateLabel = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "Success Rate",
|
||||
TextSize = 13,
|
||||
},
|
||||
successRate = new Bar
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 5,
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
},
|
||||
percentContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 0f,
|
||||
Child = successPercent = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = @"0%",
|
||||
TextSize = 13,
|
||||
},
|
||||
},
|
||||
graphLabel = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = "Points of Failure",
|
||||
TextSize = 13,
|
||||
Margin = new MarginPadding { Vertical = 20 },
|
||||
},
|
||||
},
|
||||
},
|
||||
graph = new FailRetryGraph
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
|
||||
successRate.AccentColour = colours.Green;
|
||||
successRate.BackgroundColour = colours.GrayD;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
graph.Padding = new MarginPadding { Top = header.DrawHeight };
|
||||
}
|
||||
}
|
||||
}
|
102
osu.Game/Overlays/BeatmapSetOverlay.cs
Normal file
102
osu.Game/Overlays/BeatmapSetOverlay.cs
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays.BeatmapSet;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class BeatmapSetOverlay : WaveOverlayContainer
|
||||
{
|
||||
public const float X_PADDING = 40;
|
||||
public const float RIGHT_WIDTH = 275;
|
||||
|
||||
private readonly Header header;
|
||||
private readonly Info info;
|
||||
|
||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
public BeatmapSetOverlay()
|
||||
{
|
||||
FirstWaveColour = OsuColour.Gray(0.4f);
|
||||
SecondWaveColour = OsuColour.Gray(0.3f);
|
||||
ThirdWaveColour = OsuColour.Gray(0.2f);
|
||||
FourthWaveColour = OsuColour.Gray(0.1f);
|
||||
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Width = 0.85f;
|
||||
|
||||
Masking = true;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 3,
|
||||
Offset = new Vector2(0f, 1f),
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f)
|
||||
},
|
||||
new ScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = new ReverseChildIDFillFlowContainer<Drawable>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
header = new Header(),
|
||||
info = new Info(),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
header.Picker.Beatmap.ValueChanged += b => info.Beatmap = b;
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
FadeEdgeEffectTo(0.25f, APPEAR_DURATION, Easing.In);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
State = Visibility.Hidden;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ShowBeatmapSet(BeatmapSetInfo set)
|
||||
{
|
||||
header.BeatmapSet = info.BeatmapSet = set;
|
||||
Show();
|
||||
}
|
||||
}
|
||||
}
|
@ -63,6 +63,7 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
private const float padding = 15;
|
||||
private const float message_padding = 200;
|
||||
private const float action_padding = 3;
|
||||
private const float text_size = 20;
|
||||
|
||||
private Color4 customUsernameColour;
|
||||
@ -194,6 +195,8 @@ namespace osu.Game.Overlays.Chat
|
||||
}
|
||||
}
|
||||
};
|
||||
if (message.IsAction && senderHasBackground)
|
||||
contentFlow.Colour = OsuColour.FromHex(message.Sender.Colour);
|
||||
|
||||
updateMessageContent();
|
||||
FinishTransforms(true);
|
||||
@ -206,7 +209,17 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
timestamp.Text = $@"{message.Timestamp.LocalDateTime:HH:mm:ss}";
|
||||
username.Text = $@"{message.Sender.Username}" + (senderHasBackground ? "" : ":");
|
||||
contentFlow.Text = message.Content;
|
||||
|
||||
if (message.IsAction)
|
||||
{
|
||||
contentFlow.Clear();
|
||||
contentFlow.AddText("[", sprite => sprite.Padding = new MarginPadding { Right = action_padding });
|
||||
contentFlow.AddText(message.Content, sprite => sprite.Font = @"Exo2.0-MediumItalic");
|
||||
contentFlow.AddText("]", sprite => sprite.Padding = new MarginPadding { Left = action_padding });
|
||||
}
|
||||
else
|
||||
contentFlow.Text = message.Content;
|
||||
|
||||
}
|
||||
|
||||
private class MessageSender : ClickableContainer, IHasContextMenu
|
||||
|
@ -465,7 +465,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
textbox.Text = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(postText))
|
||||
if (string.IsNullOrWhiteSpace(postText))
|
||||
return;
|
||||
|
||||
var target = currentChannel;
|
||||
@ -478,11 +478,36 @@ namespace osu.Game.Overlays
|
||||
return;
|
||||
}
|
||||
|
||||
bool isAction = false;
|
||||
|
||||
if (postText[0] == '/')
|
||||
{
|
||||
// TODO: handle commands
|
||||
target.AddNewMessages(new ErrorMessage("Chat commands are not supported yet!"));
|
||||
return;
|
||||
string[] parameters = postText.Substring(1).Split(new[] { ' ' }, 2);
|
||||
string command = parameters[0];
|
||||
string content = parameters.Length == 2 ? parameters[1] : string.Empty;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "me":
|
||||
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
currentChannel.AddNewMessages(new ErrorMessage("Usage: /me [action]"));
|
||||
return;
|
||||
}
|
||||
|
||||
isAction = true;
|
||||
postText = content;
|
||||
break;
|
||||
|
||||
case "help":
|
||||
currentChannel.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
|
||||
return;
|
||||
|
||||
default:
|
||||
currentChannel.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var message = new LocalEchoMessage
|
||||
@ -491,6 +516,7 @@ namespace osu.Game.Overlays
|
||||
Timestamp = DateTimeOffset.Now,
|
||||
TargetType = TargetType.Channel, //TODO: read this from channel
|
||||
TargetId = target.Id,
|
||||
IsAction = isAction,
|
||||
Content = postText
|
||||
};
|
||||
|
||||
|
@ -12,7 +12,6 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
@ -22,6 +21,11 @@ namespace osu.Game.Overlays.Direct
|
||||
private const float vertical_padding = 5;
|
||||
|
||||
private FillFlowContainer bottomPanel;
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
|
||||
protected override PlayButton PlayButton => playButton;
|
||||
protected override Box PreviewBar => progressBar;
|
||||
|
||||
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
|
||||
{
|
||||
@ -88,6 +92,15 @@ namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
progressBar = new Box
|
||||
{
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0, 3),
|
||||
Alpha = 0,
|
||||
Colour = colours.Yellow,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -150,6 +163,15 @@ namespace osu.Game.Overlays.Direct
|
||||
},
|
||||
},
|
||||
},
|
||||
new DownloadButton
|
||||
{
|
||||
Size = new Vector2(30),
|
||||
Margin = new MarginPadding(horizontal_padding),
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Colour = colours.Gray5,
|
||||
Action = StartDownload
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -170,13 +192,13 @@ namespace osu.Game.Overlays.Direct
|
||||
new Statistic(FontAwesome.fa_heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0),
|
||||
},
|
||||
},
|
||||
playButton = new PlayButton(SetInfo)
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5, Left = 10 },
|
||||
Size = new Vector2(30),
|
||||
Alpha = 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
StartDownload();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,8 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
@ -30,8 +28,14 @@ namespace osu.Game.Overlays.Direct
|
||||
Height = height;
|
||||
}
|
||||
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
|
||||
protected override PlayButton PlayButton => playButton;
|
||||
protected override Box PreviewBar => progressBar;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(LocalisationEngine localisation)
|
||||
private void load(LocalisationEngine localisation, OsuColour colours)
|
||||
{
|
||||
Content.CornerRadius = 5;
|
||||
|
||||
@ -50,29 +54,50 @@ namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Direction = FillDirection.Horizontal,
|
||||
LayoutEasing = Easing.OutQuint,
|
||||
LayoutDuration = 120,
|
||||
Spacing = new Vector2(10, 0),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
playButton = new PlayButton(SetInfo)
|
||||
{
|
||||
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
|
||||
TextSize = 18,
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Size = new Vector2(height / 2),
|
||||
FillMode = FillMode.Fit,
|
||||
Alpha = 0,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20,
|
||||
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
|
||||
Children = GetDifficultyIcons(),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
|
||||
TextSize = 18,
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20,
|
||||
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
|
||||
Children = GetDifficultyIcons(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
@ -128,49 +153,17 @@ namespace osu.Game.Overlays.Direct
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private class DownloadButton : OsuClickableContainer
|
||||
{
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
public DownloadButton()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
progressBar = new Box
|
||||
{
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(30),
|
||||
Icon = FontAwesome.fa_osu_chevron_down_o,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
icon.ScaleTo(0.9f, 1000, Easing.Out);
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
icon.ScaleTo(1f, 500, Easing.OutElastic);
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
icon.ScaleTo(1f, 500, Easing.OutElastic);
|
||||
}
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
BypassAutoSizeAxes = Axes.Y,
|
||||
Size = new Vector2(0, 3),
|
||||
Alpha = 0,
|
||||
Colour = colours.Yellow,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ using osu.Game.Online.API;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Audio.Track;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
@ -37,6 +39,12 @@ namespace osu.Game.Overlays.Direct
|
||||
private ProgressBar progressBar;
|
||||
private BeatmapManager beatmaps;
|
||||
private NotificationOverlay notifications;
|
||||
private BeatmapSetOverlay beatmapSetOverlay;
|
||||
|
||||
public Track Preview => PlayButton.Preview;
|
||||
public Bindable<bool> PreviewPlaying => PlayButton.Playing;
|
||||
protected abstract PlayButton PlayButton { get; }
|
||||
protected abstract Box PreviewBar { get; }
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
@ -63,11 +71,12 @@ namespace osu.Game.Overlays.Direct
|
||||
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications)
|
||||
private void load(APIAccess api, BeatmapManager beatmaps, OsuColour colours, NotificationOverlay notifications, BeatmapSetOverlay beatmapSetOverlay)
|
||||
{
|
||||
this.api = api;
|
||||
this.beatmaps = beatmaps;
|
||||
this.notifications = notifications;
|
||||
this.beatmapSetOverlay = beatmapSetOverlay;
|
||||
|
||||
AddInternal(content = new Container
|
||||
{
|
||||
@ -102,10 +111,21 @@ namespace osu.Game.Overlays.Direct
|
||||
attachDownload(downloadRequest);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (PreviewPlaying && Preview != null)
|
||||
{
|
||||
PreviewBar.Width = (float)(Preview.CurrentTime / Preview.Length);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
|
||||
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
|
||||
PlayButton.FadeIn(120, Easing.InOutQuint);
|
||||
|
||||
return base.OnHover(state);
|
||||
}
|
||||
@ -114,10 +134,21 @@ namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
|
||||
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
|
||||
if (!PreviewPlaying)
|
||||
PlayButton.FadeOut(120, Easing.InOutQuint);
|
||||
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
ShowInformation();
|
||||
PreviewPlaying.Value = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void ShowInformation() => beatmapSetOverlay?.ShowBeatmapSet(SetInfo);
|
||||
|
||||
protected void StartDownload()
|
||||
{
|
||||
if (!api.LocalUser.Value.IsSupporter)
|
||||
@ -173,6 +204,9 @@ namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
base.LoadComplete();
|
||||
this.FadeInFromZero(200, Easing.Out);
|
||||
|
||||
PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint);
|
||||
PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint);
|
||||
}
|
||||
|
||||
protected List<DifficultyIcon> GetDifficultyIcons()
|
||||
|
53
osu.Game/Overlays/Direct/DownloadButton.cs
Normal file
53
osu.Game/Overlays/Direct/DownloadButton.cs
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class DownloadButton : OsuClickableContainer
|
||||
{
|
||||
private readonly SpriteIcon icon;
|
||||
|
||||
public DownloadButton()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(30),
|
||||
Icon = FontAwesome.fa_osu_chevron_down_o,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
icon.ScaleTo(0.9f, 1000, Easing.Out);
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
icon.ScaleTo(1f, 500, Easing.OutElastic);
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
icon.ScaleTo(1.1f, 500, Easing.OutElastic);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
icon.ScaleTo(1f, 500, Easing.OutElastic);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Direct
|
||||
|
||||
public Header()
|
||||
{
|
||||
Tabs.Current.Value = DirectTab.Search;
|
||||
Tabs.Current.Value = DirectTab.NewestMaps;
|
||||
Tabs.Current.TriggerChange();
|
||||
}
|
||||
}
|
||||
|
181
osu.Game/Overlays/Direct/PlayButton.cs
Normal file
181
osu.Game/Overlays/Direct/PlayButton.cs
Normal file
@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class PlayButton : Container
|
||||
{
|
||||
public readonly Bindable<bool> Playing = new Bindable<bool>();
|
||||
public Track Preview { get; private set; }
|
||||
|
||||
private BeatmapSetInfo beatmapSet;
|
||||
public BeatmapSetInfo BeatmapSet
|
||||
{
|
||||
get { return beatmapSet; }
|
||||
set
|
||||
{
|
||||
if (value == beatmapSet) return;
|
||||
beatmapSet = value;
|
||||
|
||||
Playing.Value = false;
|
||||
trackLoader = null;
|
||||
Preview = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Color4 hoverColour;
|
||||
private readonly SpriteIcon icon;
|
||||
private readonly LoadingAnimation loadingAnimation;
|
||||
|
||||
private const float transition_duration = 500;
|
||||
|
||||
private bool loading
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
loadingAnimation.Show();
|
||||
icon.FadeOut(transition_duration * 5, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadingAnimation.Hide();
|
||||
icon.FadeIn(transition_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PlayButton(BeatmapSetInfo setInfo = null)
|
||||
{
|
||||
BeatmapSet = setInfo;
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fit,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.fa_play,
|
||||
},
|
||||
loadingAnimation = new LoadingAnimation(),
|
||||
});
|
||||
|
||||
Playing.ValueChanged += playing =>
|
||||
{
|
||||
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
|
||||
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
|
||||
updatePreviewTrack(playing);
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour)
|
||||
{
|
||||
hoverColour = colour.Yellow;
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
Playing.Value = !Playing.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
icon.FadeColour(hoverColour, 120, Easing.InOutQuint);
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
if (!Playing.Value)
|
||||
icon.FadeColour(Color4.White, 120, Easing.InOutQuint);
|
||||
base.OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Preview?.HasCompleted ?? false)
|
||||
{
|
||||
Playing.Value = false;
|
||||
Preview = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePreviewTrack(bool playing)
|
||||
{
|
||||
if (playing)
|
||||
{
|
||||
if (Preview == null)
|
||||
{
|
||||
beginAudioLoad();
|
||||
return;
|
||||
}
|
||||
|
||||
Preview.Seek(0);
|
||||
Preview.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Preview?.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private TrackLoader trackLoader;
|
||||
|
||||
private void beginAudioLoad()
|
||||
{
|
||||
if (trackLoader != null) return;
|
||||
|
||||
Add(new AsyncLoadWrapper(trackLoader = new TrackLoader($"https://b.ppy.sh/preview/{BeatmapSet.OnlineBeatmapSetID}.mp3")
|
||||
{
|
||||
OnLoadComplete = d =>
|
||||
{
|
||||
// we may have been replaced by another loader
|
||||
if (trackLoader != d) return;
|
||||
|
||||
Preview = (d as TrackLoader)?.Preview;
|
||||
Playing.TriggerChange();
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
private class TrackLoader : Drawable
|
||||
{
|
||||
private readonly string preview;
|
||||
|
||||
public Track Preview;
|
||||
|
||||
public TrackLoader(string preview)
|
||||
{
|
||||
this.preview = preview;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(preview))
|
||||
{
|
||||
Preview = audio.Track.Get(preview);
|
||||
Preview.Volume.Value = 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@ namespace osu.Game.Overlays
|
||||
private readonly FillFlowContainer resultCountsContainer;
|
||||
private readonly OsuSpriteText resultCountsText;
|
||||
private FillFlowContainer<DirectPanel> panels;
|
||||
private DirectPanel playing;
|
||||
|
||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"485e74");
|
||||
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"465b71");
|
||||
@ -201,6 +202,12 @@ namespace osu.Game.Overlays
|
||||
panels.FadeOut(200);
|
||||
panels.Expire();
|
||||
panels = null;
|
||||
|
||||
if (playing != null)
|
||||
{
|
||||
playing.PreviewPlaying.Value = false;
|
||||
playing = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (BeatmapSets == null) return;
|
||||
@ -227,6 +234,17 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
if (panels != null) ScrollFlow.Remove(panels);
|
||||
ScrollFlow.Add(panels = newPanels);
|
||||
|
||||
foreach (DirectPanel panel in p.Children)
|
||||
panel.PreviewPlaying.ValueChanged += newValue =>
|
||||
{
|
||||
if (newValue)
|
||||
{
|
||||
if (playing != null && playing != panel)
|
||||
playing.PreviewPlaying.Value = false;
|
||||
playing = panel;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@ -251,7 +269,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return;
|
||||
|
||||
getSetsRequest = new GetBeatmapSetsRequest(currentQuery,
|
||||
getSetsRequest = new GetBeatmapSetsRequest(currentQuery.Value ?? string.Empty,
|
||||
((FilterControl)Filter).Ruleset.Value,
|
||||
Filter.DisplayStyleControl.Dropdown.Current.Value,
|
||||
Filter.Tabs.Current.Value); //todo: sort direction (?)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Settings.Sections.General;
|
||||
using OpenTK.Graphics;
|
||||
@ -28,35 +29,43 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f,
|
||||
},
|
||||
new OsuContextMenuContainer
|
||||
{
|
||||
Width = 360,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Masking = true,
|
||||
AutoSizeDuration = transition_time,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
settingsSection = new LoginSettings
|
||||
{
|
||||
Padding = new MarginPadding(10),
|
||||
RequestHide = Hide,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Height = 3,
|
||||
Colour = colours.Yellow,
|
||||
Alpha = 1,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Masking = true,
|
||||
AutoSizeDuration = transition_time,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
settingsSection = new LoginSettings
|
||||
{
|
||||
Padding = new MarginPadding(10),
|
||||
RequestHide = Hide,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Height = 3,
|
||||
Colour = colours.Yellow,
|
||||
Alpha = 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -159,7 +159,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ namespace osu.Game.Overlays.MedalSplash
|
||||
},
|
||||
description = new TextFlowContainer
|
||||
{
|
||||
TextAnchor = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -115,15 +116,16 @@ namespace osu.Game.Overlays.MedalSplash
|
||||
medalSprite.Texture = textures.Get(medal.ImageUrl);
|
||||
medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow");
|
||||
description.Colour = colours.BlueLight;
|
||||
|
||||
unlocked.Position = new Vector2(0f, medalContainer.Size.Y / 2 + 10);
|
||||
infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updateState();
|
||||
|
||||
unlocked.Position = new Vector2(0f, medalContainer.DrawSize.Y / 2 + 10);
|
||||
infoFlow.Position = new Vector2(0f, unlocked.Position.Y + 90);
|
||||
}
|
||||
|
||||
public DisplayState State
|
||||
@ -172,6 +174,7 @@ namespace osu.Game.Overlays.MedalSplash
|
||||
|
||||
this.ScaleTo(scale_when_full, duration, Easing.OutExpo);
|
||||
this.MoveToY(MedalOverlay.DISC_SIZE / 2 - 60, duration, Easing.OutExpo);
|
||||
unlocked.Show();
|
||||
name.FadeInFromZero(duration + 100);
|
||||
description.FadeInFromZero(duration * 2);
|
||||
break;
|
||||
|
@ -127,7 +127,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
if (mod == null)
|
||||
{
|
||||
Mods = new Mod[0];
|
||||
Mods = Array.Empty<Mod>();
|
||||
Alpha = 0;
|
||||
}
|
||||
else
|
||||
|
@ -161,11 +161,15 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
prevButton = new IconButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Action = prev,
|
||||
Icon = FontAwesome.fa_step_backward,
|
||||
},
|
||||
playButton = new IconButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1.4f),
|
||||
IconScale = new Vector2(1.4f),
|
||||
Action = play,
|
||||
@ -173,6 +177,8 @@ namespace osu.Game.Overlays
|
||||
},
|
||||
nextButton = new IconButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Action = next,
|
||||
Icon = FontAwesome.fa_step_forward,
|
||||
},
|
||||
|
@ -19,6 +19,7 @@ using osu.Game.Users;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
|
||||
namespace osu.Game.Overlays.Profile
|
||||
{
|
||||
@ -119,15 +120,11 @@ namespace osu.Game.Overlays.Profile
|
||||
}
|
||||
}
|
||||
},
|
||||
new LinkFlowContainer.LinkText
|
||||
new LinkFlowContainer.ProfileLink(user)
|
||||
{
|
||||
Text = user.Username,
|
||||
Url = $@"https://osu.ppy.sh/users/{user.Id}",
|
||||
TextSize = 30,
|
||||
Font = @"Exo2.0-RegularItalic",
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Y = -48
|
||||
Y = -48,
|
||||
},
|
||||
countryFlag = new DrawableFlag(user.Country?.FlagName)
|
||||
{
|
||||
@ -534,6 +531,19 @@ namespace osu.Game.Overlays.Profile
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ProfileLink : LinkText, IHasTooltip
|
||||
{
|
||||
public string TooltipText => "View Profile in Browser";
|
||||
|
||||
public ProfileLink(User user)
|
||||
{
|
||||
Text = user.Username;
|
||||
Url = $@"https://osu.ppy.sh/users/{user.Id}";
|
||||
Font = @"Exo2.0-RegularItalic";
|
||||
TextSize = 30;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,30 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Configuration;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
{
|
||||
public class DetailSettings : SettingsSubsection
|
||||
{
|
||||
protected override string Header => "Detail Settings";
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Storyboards",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowStoryboard)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Rotate cursor when dragging",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.CursorRotation)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
public const float CONTENT_X_MARGIN = 50;
|
||||
|
||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -165,10 +164,8 @@ namespace osu.Game.Overlays
|
||||
wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y));
|
||||
}
|
||||
|
||||
private class Wave : Container, IStateful<Visibility>
|
||||
private class Wave : VisibilityContainer
|
||||
{
|
||||
public event Action<Visibility> StateChanged;
|
||||
|
||||
public float FinalPosition;
|
||||
|
||||
public Wave()
|
||||
@ -183,13 +180,7 @@ namespace osu.Game.Overlays
|
||||
Radius = 20f,
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
};
|
||||
Child = new Box { RelativeSizeAxes = Axes.Both };
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -201,28 +192,8 @@ namespace osu.Game.Overlays
|
||||
Height = Parent.Parent.DrawSize.Y * 1.5f;
|
||||
}
|
||||
|
||||
private Visibility state;
|
||||
|
||||
public Visibility State
|
||||
{
|
||||
get { return state; }
|
||||
set
|
||||
{
|
||||
state = value;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case Visibility.Hidden:
|
||||
this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
|
||||
break;
|
||||
case Visibility.Visible:
|
||||
this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
|
||||
break;
|
||||
}
|
||||
|
||||
StateChanged?.Invoke(State);
|
||||
}
|
||||
}
|
||||
protected override void PopIn() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show);
|
||||
protected override void PopOut() => this.MoveToY(Parent.Parent.DrawSize.Y, DISAPPEAR_DURATION, easing_hide);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </summary>
|
||||
public virtual Color4 AccentColour { get; set; } = Color4.Gray;
|
||||
|
||||
/// <summary>
|
||||
/// Whether a visible judgement should be displayed when this representation is hit.
|
||||
/// </summary>
|
||||
public virtual bool DisplayJudgement => true;
|
||||
|
||||
protected DrawableHitObject(HitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user