mirror of
https://github.com/ppy/osu.git
synced 2026-05-28 20:40:46 +08:00
Merge branch 'refs/heads/master' into release
This commit is contained in:
+1
-1
Submodule osu-framework updated: 71bbc98060...e611e186e3
+1
-1
Submodule osu-resources updated: 911564f95a...6edd5eacdd
@@ -14,9 +14,9 @@ using osu.Game;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Chat.Display;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.Chat.Drawables;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
@@ -45,7 +45,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
if (api.State != APIAccess.APIState.Online)
|
||||
if (api.State != APIState.Online)
|
||||
api.OnStateChange += delegate { initializeChannels(); };
|
||||
else
|
||||
initializeChannels();
|
||||
@@ -65,7 +65,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
careChannels = new List<Channel>();
|
||||
|
||||
if (api.State != APIAccess.APIState.Online)
|
||||
if (api.State != APIState.Online)
|
||||
return;
|
||||
|
||||
Add(flow = new FlowContainer
|
||||
|
||||
@@ -12,6 +12,7 @@ using osu.Game.Modes.Objects.Drawables;
|
||||
using osu.Game.Modes.Osu.Objects;
|
||||
using osu.Game.Modes.Osu.Objects.Drawables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Modes;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
@@ -31,7 +32,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
|
||||
Clock.ProcessFrame();
|
||||
|
||||
Container approachContainer = new Container { Depth = float.MaxValue, };
|
||||
Container approachContainer = new Container { Depth = float.MinValue, };
|
||||
|
||||
Add(approachContainer);
|
||||
|
||||
@@ -49,8 +50,8 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Depth = -i,
|
||||
State = ArmedState.Armed,
|
||||
Depth = i,
|
||||
State = ArmedState.Hit,
|
||||
};
|
||||
|
||||
approachContainer.Add(d.ApproachCircle.CreateProxy());
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.GameModes.Testing;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Screens.Menu;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
@@ -15,6 +18,11 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
ColourInfo = ColourInfo.GradientVertical(Color4.Gray, Color4.WhiteSmoke),
|
||||
RelativeSizeAxes = Framework.Graphics.Axes.Both,
|
||||
});
|
||||
Add(new ButtonSystem());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Desktop.VisualTests.Platform;
|
||||
using osu.Framework.GameModes.Testing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
class TestCasePlaySongSelect : TestCase
|
||||
{
|
||||
private BeatmapDatabase db;
|
||||
private TestStorage storage;
|
||||
|
||||
public override string Name => @"Song Select";
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Desktop.VisualTests.Platform;
|
||||
using osu.Framework.GameModes.Testing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
class TestCasePlaySongSelect : TestCase
|
||||
{
|
||||
private BeatmapDatabase db, oldDb;
|
||||
private TestStorage storage;
|
||||
|
||||
public override string Name => @"Song Select";
|
||||
public override string Description => @"with fake data";
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
oldDb = Dependencies.Get<BeatmapDatabase>();
|
||||
if (db == null)
|
||||
{
|
||||
storage = new TestStorage(@"TestCasePlaySongSelect");
|
||||
db = new BeatmapDatabase(storage);
|
||||
Dependencies.Cache(db, true);
|
||||
|
||||
var sets = new List<BeatmapSetInfo>();
|
||||
|
||||
@@ -34,8 +35,14 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
|
||||
db.Import(sets);
|
||||
}
|
||||
|
||||
Add(new PlaySongSelect(db));
|
||||
Add(new PlaySongSelect());
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (oldDb != null)
|
||||
Dependencies.Cache(oldDb, true);
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
private BeatmapSetInfo createTestBeatmapSet(int i)
|
||||
@@ -89,6 +96,6 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.GameModes.Testing;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Framework.Timing;
|
||||
@@ -9,6 +10,8 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Modes.Objects;
|
||||
using osu.Game.Modes.Osu.Objects;
|
||||
using osu.Game.Screens.Play;
|
||||
@@ -18,10 +21,17 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
class TestCasePlayer : TestCase
|
||||
{
|
||||
private WorkingBeatmap beatmap;
|
||||
public override string Name => @"Player";
|
||||
|
||||
public override string Description => @"Showing everything to play the game.";
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapDatabase db)
|
||||
{
|
||||
beatmap = db.GetWorkingBeatmap(db.Query<BeatmapInfo>().Where(b => b.Mode == PlayMode.Osu).FirstOrDefault());
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
@@ -29,31 +39,37 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
//ensure we are at offset 0
|
||||
Clock = new FramedClock();
|
||||
|
||||
var objects = new List<HitObject>();
|
||||
|
||||
int time = 1500;
|
||||
for (int i = 0; i < 50; i++)
|
||||
if (beatmap == null)
|
||||
{
|
||||
objects.Add(new HitCircle()
|
||||
|
||||
var objects = new List<HitObject>();
|
||||
|
||||
int time = 1500;
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
StartTime = time,
|
||||
Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : 512,
|
||||
i % 4 < 2 ? 0 : 384),
|
||||
NewCombo = i % 4 == 0
|
||||
});
|
||||
objects.Add(new HitCircle()
|
||||
{
|
||||
StartTime = time,
|
||||
Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : 512,
|
||||
i % 4 < 2 ? 0 : 384),
|
||||
NewCombo = i % 4 == 0
|
||||
});
|
||||
|
||||
time += 500;
|
||||
time += 500;
|
||||
}
|
||||
|
||||
var decoder = new ConstructableBeatmapDecoder();
|
||||
|
||||
Beatmap b = new Beatmap
|
||||
{
|
||||
HitObjects = objects
|
||||
};
|
||||
|
||||
decoder.Process(b);
|
||||
|
||||
beatmap = new WorkingBeatmap(b);
|
||||
}
|
||||
|
||||
var decoder = new ConstructableBeatmapDecoder();
|
||||
|
||||
Beatmap b = new Beatmap
|
||||
{
|
||||
HitObjects = objects
|
||||
};
|
||||
|
||||
decoder.Process(b);
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
RelativeSizeAxes = Framework.Graphics.Axes.Both,
|
||||
@@ -62,7 +78,8 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
|
||||
Add(new Player
|
||||
{
|
||||
Beatmap = new WorkingBeatmap(b)
|
||||
PreferredPlayMode = PlayMode.Osu,
|
||||
Beatmap = beatmap
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects
|
||||
{
|
||||
public class BezierApproximator
|
||||
{
|
||||
private int count;
|
||||
private List<Vector2> controlPoints;
|
||||
private Vector2[] subdivisionBuffer1;
|
||||
private Vector2[] subdivisionBuffer2;
|
||||
|
||||
private const float TOLERANCE = 0.5f;
|
||||
private const float TOLERANCE_SQ = TOLERANCE * TOLERANCE;
|
||||
|
||||
public BezierApproximator(List<Vector2> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
count = controlPoints.Count;
|
||||
|
||||
subdivisionBuffer1 = new Vector2[count];
|
||||
subdivisionBuffer2 = new Vector2[count * 2 - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure the 2nd order derivative (approximated using finite elements) is within tolerable bounds.
|
||||
/// NOTE: The 2nd order derivative of a 2d curve represents its curvature, so intuitively this function
|
||||
/// checks (as the name suggests) whether our approximation is _locally_ "flat". More curvy parts
|
||||
/// need to have a denser approximation to be more "flat".
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points to check for flatness.</param>
|
||||
/// <returns>Whether the control points are flat enough.</returns>
|
||||
private static bool IsFlatEnough(Vector2[] controlPoints)
|
||||
{
|
||||
for (int i = 1; i < controlPoints.Length - 1; i++)
|
||||
if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > TOLERANCE_SQ)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subdivides n control points representing a bezier curve into 2 sets of n control points, each
|
||||
/// describing a bezier curve equivalent to a half of the original curve. Effectively this splits
|
||||
/// the original curve into 2 curves which result in the original curve when pieced back together.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points to split.</param>
|
||||
/// <param name="l">Output: The control points corresponding to the left half of the curve.</param>
|
||||
/// <param name="r">Output: The control points corresponding to the right half of the curve.</param>
|
||||
private void Subdivide(Vector2[] controlPoints, Vector2[] l, Vector2[] r)
|
||||
{
|
||||
Vector2[] midpoints = subdivisionBuffer1;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
midpoints[i] = controlPoints[i];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
l[i] = midpoints[0];
|
||||
r[count - i - 1] = midpoints[count - i - 1];
|
||||
|
||||
for (int j = 0; j < count - i - 1; j++)
|
||||
midpoints[j] = (midpoints[j] + midpoints[j + 1]) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This uses <a href="https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm">De Casteljau's algorithm</a> to obtain an optimal
|
||||
/// piecewise-linear approximation of the bezier curve with the same amount of points as there are control points.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points describing the bezier curve to be approximated.</param>
|
||||
/// <param name="output">The points representing the resulting piecewise-linear approximation.</param>
|
||||
private void Approximate(Vector2[] controlPoints, List<Vector2> output)
|
||||
{
|
||||
Vector2[] l = subdivisionBuffer2;
|
||||
Vector2[] r = subdivisionBuffer1;
|
||||
|
||||
Subdivide(controlPoints, l, r);
|
||||
|
||||
for (int i = 0; i < count - 1; ++i)
|
||||
l[count + i] = r[i + 1];
|
||||
|
||||
output.Add(controlPoints[0]);
|
||||
for (int i = 1; i < count - 1; ++i)
|
||||
{
|
||||
int index = 2 * i;
|
||||
Vector2 p = 0.25f * (l[index - 1] + 2 * l[index] + l[index + 1]);
|
||||
output.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing
|
||||
/// the control points until their approximation error vanishes below a given threshold.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points describing the curve.</param>
|
||||
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
||||
public List<Vector2> CreateBezier()
|
||||
{
|
||||
List<Vector2> output = new List<Vector2>();
|
||||
|
||||
if (count == 0)
|
||||
return output;
|
||||
|
||||
Stack<Vector2[]> toFlatten = new Stack<Vector2[]>();
|
||||
Stack<Vector2[]> freeBuffers = new Stack<Vector2[]>();
|
||||
|
||||
// "toFlatten" contains all the curves which are not yet approximated well enough.
|
||||
// We use a stack to emulate recursion without the risk of running into a stack overflow.
|
||||
// (More specifically, we iteratively and adaptively refine our curve with a
|
||||
// <a href="https://en.wikipedia.org/wiki/Depth-first_search">Depth-first search</a>
|
||||
// over the tree resulting from the subdivisions we make.)
|
||||
toFlatten.Push(controlPoints.ToArray());
|
||||
|
||||
Vector2[] leftChild = subdivisionBuffer2;
|
||||
|
||||
while (toFlatten.Count > 0)
|
||||
{
|
||||
Vector2[] parent = toFlatten.Pop();
|
||||
if (IsFlatEnough(parent))
|
||||
{
|
||||
// If the control points we currently operate on are sufficiently "flat", we use
|
||||
// an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation
|
||||
// of the bezier curve represented by our control points, consisting of the same amount
|
||||
// of points as there are control points.
|
||||
Approximate(parent, output);
|
||||
freeBuffers.Push(parent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep
|
||||
// subdividing the curve we are currently operating on.
|
||||
Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count];
|
||||
Subdivide(parent, leftChild, rightChild);
|
||||
|
||||
// We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration.
|
||||
for (int i = 0; i < count; ++i)
|
||||
parent[i] = leftChild[i];
|
||||
|
||||
toFlatten.Push(rightChild);
|
||||
toFlatten.Push(parent);
|
||||
}
|
||||
|
||||
output.Add(controlPoints[count - 1]);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Game.Modes.Objects.Drawables;
|
||||
@@ -10,7 +11,7 @@ using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableHitCircle : DrawableHitObject
|
||||
public class DrawableHitCircle : DrawableOsuHitObject
|
||||
{
|
||||
private OsuHitObject osuObject;
|
||||
|
||||
@@ -23,7 +24,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
private GlowPiece glow;
|
||||
private HitExplosion explosion;
|
||||
|
||||
public DrawableHitCircle(HitCircle h) : base(h)
|
||||
public DrawableHitCircle(OsuHitObject h) : base(h)
|
||||
{
|
||||
osuObject = h;
|
||||
|
||||
@@ -39,7 +40,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
circle = new CirclePiece
|
||||
{
|
||||
Colour = osuObject.Colour,
|
||||
Hit = Hit,
|
||||
Hit = () =>
|
||||
{
|
||||
((PositionalJudgementInfo)Judgement).PositionOffset = Vector2.Zero; //todo: set to correct value
|
||||
UpdateJudgement(true);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
number = new NumberPiece(),
|
||||
ring = new RingPiece(),
|
||||
@@ -53,29 +59,54 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
Colour = osuObject.Colour,
|
||||
}
|
||||
};
|
||||
|
||||
//may not be so correct
|
||||
Size = circle.DrawSize;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
//may not be so correct
|
||||
Size = circle.DrawSize;
|
||||
|
||||
//force application of the state that was set before we loaded.
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
double hit50 = 150;
|
||||
double hit100 = 80;
|
||||
double hit300 = 30;
|
||||
|
||||
protected override void CheckJudgement(bool userTriggered)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (Judgement.TimeOffset > hit50)
|
||||
Judgement.Result = HitResult.Miss;
|
||||
return;
|
||||
}
|
||||
|
||||
Flush(true); //move to DrawableHitObject
|
||||
ApproachCircle.Flush(true);
|
||||
double hitOffset = Math.Abs(Judgement.TimeOffset);
|
||||
|
||||
double t = HitTime ?? osuObject.StartTime;
|
||||
if (hitOffset < hit50)
|
||||
{
|
||||
Judgement.Result = HitResult.Hit;
|
||||
|
||||
Alpha = 0;
|
||||
OsuJudgementInfo osuInfo = Judgement as OsuJudgementInfo;
|
||||
|
||||
if (hitOffset < hit300)
|
||||
osuInfo.Score = OsuScoreResult.Hit300;
|
||||
else if (hitOffset < hit100)
|
||||
osuInfo.Score = OsuScoreResult.Hit100;
|
||||
else if (hitOffset < hit50)
|
||||
osuInfo.Score = OsuScoreResult.Hit50;
|
||||
}
|
||||
else
|
||||
Judgement.Result = HitResult.Miss;
|
||||
}
|
||||
|
||||
protected override void UpdateInitialState()
|
||||
{
|
||||
base.UpdateInitialState();
|
||||
|
||||
//sane defaults
|
||||
ring.Alpha = circle.Alpha = number.Alpha = glow.Alpha = 1;
|
||||
@@ -83,34 +114,48 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
ApproachCircle.Scale = new Vector2(2);
|
||||
explode.Alpha = 0;
|
||||
Scale = new Vector2(0.5f); //this will probably need to be moved to DrawableHitObject at some point.
|
||||
}
|
||||
|
||||
const float preempt = 600;
|
||||
protected override void UpdatePreemptState()
|
||||
{
|
||||
base.UpdatePreemptState();
|
||||
|
||||
const float fadein = 400;
|
||||
ApproachCircle.FadeIn(Math.Min(TIME_FADEIN * 2, TIME_PREEMPT));
|
||||
ApproachCircle.ScaleTo(0.6f, TIME_PREEMPT);
|
||||
}
|
||||
|
||||
Delay(t - Time.Current - preempt, true);
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
FadeIn(fadein);
|
||||
|
||||
ApproachCircle.FadeIn(Math.Min(fadein * 2, preempt));
|
||||
ApproachCircle.ScaleTo(0.6f, preempt);
|
||||
|
||||
Delay(preempt, true);
|
||||
base.UpdateState(state);
|
||||
|
||||
ApproachCircle.FadeOut();
|
||||
|
||||
glow.FadeOut(400);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Disarmed:
|
||||
Delay(osuObject.Duration + 200);
|
||||
FadeOut(200);
|
||||
case ArmedState.Idle:
|
||||
Delay(osuObject.Duration + TIME_PREEMPT);
|
||||
FadeOut(TIME_FADEOUT);
|
||||
|
||||
explosion?.Expire();
|
||||
explosion = null;
|
||||
break;
|
||||
case ArmedState.Armed:
|
||||
case ArmedState.Miss:
|
||||
ring.FadeOut();
|
||||
circle.FadeOut();
|
||||
number.FadeOut();
|
||||
glow.FadeOut();
|
||||
|
||||
explosion?.Expire();
|
||||
explosion = null;
|
||||
|
||||
Schedule(() => Add(explosion = new HitExplosion((OsuJudgementInfo)Judgement)));
|
||||
|
||||
FadeOut(800);
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
const double flash_in = 30;
|
||||
|
||||
flash.FadeTo(0.8f, flash_in);
|
||||
@@ -119,7 +164,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
|
||||
explode.FadeIn(flash_in);
|
||||
|
||||
Schedule(() => Add(explosion = new HitExplosion(Judgement.Hit300)));
|
||||
Schedule(() => Add(explosion = new HitExplosion((OsuJudgementInfo)Judgement)));
|
||||
|
||||
Delay(flash_in, true);
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Modes.Objects;
|
||||
using osu.Game.Modes.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableOsuHitObject : DrawableHitObject
|
||||
{
|
||||
protected const float TIME_PREEMPT = 600;
|
||||
protected const float TIME_FADEIN = 400;
|
||||
protected const float TIME_FADEOUT = 500;
|
||||
|
||||
public DrawableOsuHitObject(OsuHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo();
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
|
||||
Flush(true);
|
||||
|
||||
UpdateInitialState();
|
||||
|
||||
Delay(HitObject.StartTime - Time.Current - TIME_PREEMPT + Judgement.TimeOffset, true);
|
||||
|
||||
UpdatePreemptState();
|
||||
|
||||
Delay(TIME_PREEMPT, true);
|
||||
}
|
||||
|
||||
protected virtual void UpdatePreemptState()
|
||||
{
|
||||
FadeIn(TIME_FADEIN);
|
||||
}
|
||||
|
||||
protected virtual void UpdateInitialState()
|
||||
{
|
||||
Alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class OsuJudgementInfo : PositionalJudgementInfo
|
||||
{
|
||||
public OsuScoreResult Score;
|
||||
public ComboResult Combo;
|
||||
}
|
||||
|
||||
public enum ComboResult
|
||||
{
|
||||
[Description(@"")]
|
||||
None,
|
||||
[Description(@"Good")]
|
||||
Good,
|
||||
[Description(@"Amazing")]
|
||||
Perfect
|
||||
}
|
||||
|
||||
public enum OsuScoreResult
|
||||
{
|
||||
[Description(@"Miss")]
|
||||
Miss,
|
||||
[Description(@"50")]
|
||||
Hit50,
|
||||
[Description(@"100")]
|
||||
Hit100,
|
||||
[Description(@"300")]
|
||||
Hit300,
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,52 @@
|
||||
using osu.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Modes.Objects.Drawables;
|
||||
using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
{
|
||||
class DrawableSlider : DrawableHitObject
|
||||
class DrawableSlider : DrawableOsuHitObject
|
||||
{
|
||||
public DrawableSlider(Slider h) : base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
Position = new Vector2(h.Position.X / 512, h.Position.Y / 384);
|
||||
private Slider slider;
|
||||
|
||||
for (float i = 0; i <= 1; i += 0.1f)
|
||||
private DrawableHitCircle startCircle;
|
||||
private Container ball;
|
||||
private Body body;
|
||||
|
||||
public DrawableSlider(Slider s) : base(s)
|
||||
{
|
||||
slider = s;
|
||||
|
||||
Origin = Anchor.TopLeft;
|
||||
Position = Vector2.Zero;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Add(new CirclePiece
|
||||
body = new Body(s)
|
||||
{
|
||||
Colour = h.Colour,
|
||||
Hit = Hit,
|
||||
Position = h.Curve.PositionAt(i) - h.Position //non-relative?
|
||||
});
|
||||
}
|
||||
Position = s.Position,
|
||||
},
|
||||
ball = new Ball(),
|
||||
startCircle = new DrawableHitCircle(new HitCircle
|
||||
{
|
||||
StartTime = s.StartTime,
|
||||
Position = s.Position,
|
||||
Colour = s.Colour,
|
||||
})
|
||||
{
|
||||
Depth = -1 //override time-based depth.
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -32,19 +57,234 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
UpdateState(State);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
ball.Alpha = Time.Current >= slider.StartTime && Time.Current <= slider.EndTime ? 1 : 0;
|
||||
|
||||
double t = (Time.Current - slider.StartTime) / slider.Duration;
|
||||
if (slider.RepeatCount > 1)
|
||||
{
|
||||
int currentRepeat = (int)(t * slider.RepeatCount);
|
||||
t = (t * slider.RepeatCount) % 1;
|
||||
if (currentRepeat % 2 == 1)
|
||||
t = 1 - t;
|
||||
}
|
||||
|
||||
ball.Position = slider.Curve.PositionAt(t);
|
||||
}
|
||||
|
||||
protected override void CheckJudgement(bool userTriggered)
|
||||
{
|
||||
var j = Judgement as OsuJudgementInfo;
|
||||
var sc = startCircle.Judgement as OsuJudgementInfo;
|
||||
|
||||
if (!userTriggered && Time.Current >= HitObject.EndTime)
|
||||
{
|
||||
j.Score = sc.Score;
|
||||
j.Result = sc.Result;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
if (!IsLoaded) return;
|
||||
base.UpdateState(state);
|
||||
|
||||
Flush(true); //move to DrawableHitObject
|
||||
Delay(HitObject.Duration);
|
||||
FadeOut(300);
|
||||
}
|
||||
|
||||
Alpha = 0;
|
||||
private class Ball : Container
|
||||
{
|
||||
private Box follow;
|
||||
|
||||
Delay(HitObject.StartTime - 200 - Time.Current, true);
|
||||
public Ball()
|
||||
{
|
||||
Masking = true;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
BlendingMode = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
FadeIn(200);
|
||||
Delay(200 + HitObject.Duration);
|
||||
FadeOut(200);
|
||||
Children = new Drawable[]
|
||||
{
|
||||
follow = new Box
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Colour = Color4.Orange,
|
||||
Width = 64,
|
||||
Height = 64,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Masking = true,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Colour = Color4.Cyan,
|
||||
CornerRadius = 32,
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
|
||||
Width = 64,
|
||||
Height = 64,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private InputState lastState;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
lastState = state;
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
lastState = state;
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
lastState = state;
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
bool tracking;
|
||||
protected bool Tracking
|
||||
{
|
||||
get { return tracking; }
|
||||
set
|
||||
{
|
||||
if (value == tracking) return;
|
||||
|
||||
tracking = value;
|
||||
|
||||
follow.ScaleTo(tracking ? 2.4f : 1, 140, EasingTypes.Out);
|
||||
follow.FadeTo(tracking ? 0.8f : 0, 140, EasingTypes.Out);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
CornerRadius = DrawWidth / 2;
|
||||
Tracking = lastState != null && Contains(lastState.Mouse.NativeState.Position) && lastState.Mouse.HasMainButtonPressed;
|
||||
}
|
||||
}
|
||||
|
||||
private class Body : Container
|
||||
{
|
||||
private Path path;
|
||||
private BufferedContainer container;
|
||||
|
||||
private double? drawnProgress;
|
||||
|
||||
private Slider slider;
|
||||
public Body(Slider s)
|
||||
{
|
||||
slider = s;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
container = new BufferedContainer
|
||||
{
|
||||
CacheDrawnFrameBuffer = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
path = new Path
|
||||
{
|
||||
Colour = s.Colour,
|
||||
BlendingMode = BlendingMode.None,
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
container.Attach(RenderbufferInternalFormat.DepthComponent16);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
// Surprisingly, this looks somewhat okay and works well as a test for self-overlaps.
|
||||
// TODO: Don't do this.
|
||||
path.Texture = textures.Get(@"Menu/logo");
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
path.PathWidth = 32;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (updateSnaking())
|
||||
{
|
||||
// Autosizing does not give us the desired behaviour here.
|
||||
// We want the container to have the same size as the slider,
|
||||
// and to be positioned such that the slider head is at (0,0).
|
||||
container.Size = path.Size;
|
||||
container.Position = -path.HeadPosition;
|
||||
|
||||
container.ForceRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
private bool updateSnaking()
|
||||
{
|
||||
double progress = MathHelper.Clamp((Time.Current - slider.StartTime + TIME_PREEMPT) / TIME_FADEIN, 0, 1);
|
||||
|
||||
if (progress == drawnProgress) return false;
|
||||
|
||||
bool madeChanges = false;
|
||||
if (progress == 0)
|
||||
{
|
||||
//if we have gone backwards, just clear the path for now.
|
||||
drawnProgress = 0;
|
||||
path.ClearVertices();
|
||||
madeChanges = true;
|
||||
}
|
||||
|
||||
Vector2 startPosition = slider.Curve.PositionAt(0);
|
||||
|
||||
if (drawnProgress == null)
|
||||
{
|
||||
drawnProgress = 0;
|
||||
path.AddVertex(slider.Curve.PositionAt(drawnProgress.Value) - startPosition);
|
||||
madeChanges = true;
|
||||
}
|
||||
|
||||
double segmentSize = 1 / (slider.Curve.Length / 5);
|
||||
|
||||
while (drawnProgress + segmentSize < progress)
|
||||
{
|
||||
drawnProgress += segmentSize;
|
||||
path.AddVertex(slider.Curve.PositionAt(drawnProgress.Value) - startPosition);
|
||||
madeChanges = true;
|
||||
}
|
||||
|
||||
if (progress == 1 && drawnProgress != progress)
|
||||
{
|
||||
drawnProgress = progress;
|
||||
path.AddVertex(slider.Curve.PositionAt(drawnProgress.Value) - startPosition);
|
||||
madeChanges = true;
|
||||
}
|
||||
|
||||
return madeChanges;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Game.Modes.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
@@ -12,7 +13,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
private SpriteText line1;
|
||||
private SpriteText line2;
|
||||
|
||||
public HitExplosion(Judgement judgement, ComboJudgement comboJudgement = ComboJudgement.None)
|
||||
public HitExplosion(OsuJudgementInfo judgement)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Anchor = Anchor.Centre;
|
||||
@@ -27,13 +28,13 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Text = judgement.GetDescription(),
|
||||
Text = judgement.Score.GetDescription(),
|
||||
Font = @"Venera",
|
||||
TextSize = 20,
|
||||
},
|
||||
line2 = new SpriteText
|
||||
{
|
||||
Text = comboJudgement.GetDescription(),
|
||||
Text = judgement.Combo.GetDescription(),
|
||||
Font = @"Venera",
|
||||
TextSize = 14,
|
||||
}
|
||||
|
||||
@@ -49,8 +49,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
Hit?.Invoke();
|
||||
return true;
|
||||
return Hit?.Invoke() ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,29 +8,24 @@ using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class Triangles : Container
|
||||
public class Triangles : Container<Triangle>
|
||||
{
|
||||
private Texture triangle;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
triangle = textures.Get(@"Play/osu/triangle@2x");
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
const float size = 100;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Add(new Sprite
|
||||
Add(new Triangle
|
||||
{
|
||||
Texture = triangle,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()),
|
||||
Scale = new Vector2(RNG.NextSingle() * 0.4f + 0.2f),
|
||||
Alpha = RNG.NextSingle() * 0.3f
|
||||
// Scaling height by 0.866 results in equiangular triangles (== 60° and equal side length)
|
||||
Size = new Vector2(size, 0.866f * size),
|
||||
Alpha = RNG.NextSingle() * 0.3f,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,198 +1,26 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Beatmaps;
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects
|
||||
{
|
||||
public class Slider : OsuHitObject
|
||||
{
|
||||
public override double EndTime => StartTime + (RepeatCount + 1) * Curve.Length;
|
||||
public override double EndTime => StartTime + RepeatCount * Curve.Length / Velocity;
|
||||
|
||||
public double Velocity;
|
||||
|
||||
public override void SetDefaultsFromBeatmap(Beatmap beatmap)
|
||||
{
|
||||
Velocity = 100 / beatmap.BeatLengthAt(StartTime, true) * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier;
|
||||
}
|
||||
|
||||
public int RepeatCount;
|
||||
|
||||
public SliderCurve Curve;
|
||||
|
||||
}
|
||||
|
||||
public class SliderCurve
|
||||
{
|
||||
public double Length;
|
||||
|
||||
public List<Vector2> Path;
|
||||
|
||||
public CurveTypes CurveType;
|
||||
|
||||
private List<Vector2> calculatedPath;
|
||||
|
||||
public void Calculate()
|
||||
{
|
||||
switch (CurveType)
|
||||
{
|
||||
case CurveTypes.Linear:
|
||||
calculatedPath = Path;
|
||||
break;
|
||||
default:
|
||||
var bezier = new BezierApproximator(Path);
|
||||
calculatedPath = bezier.CreateBezier();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress)
|
||||
{
|
||||
int index = (int)(progress * (calculatedPath.Count - 1));
|
||||
|
||||
Vector2 pos = calculatedPath[index];
|
||||
if (index != progress)
|
||||
pos += (calculatedPath[index + 1] - pos) * (float)(progress - index);
|
||||
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
public class BezierApproximator
|
||||
{
|
||||
private int count;
|
||||
private List<Vector2> controlPoints;
|
||||
private Vector2[] subdivisionBuffer1;
|
||||
private Vector2[] subdivisionBuffer2;
|
||||
|
||||
private const float TOLERANCE = 0.5f;
|
||||
private const float TOLERANCE_SQ = TOLERANCE * TOLERANCE;
|
||||
|
||||
public BezierApproximator(List<Vector2> controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
count = controlPoints.Count;
|
||||
|
||||
subdivisionBuffer1 = new Vector2[count];
|
||||
subdivisionBuffer2 = new Vector2[count * 2 - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure the 2nd order derivative (approximated using finite elements) is within tolerable bounds.
|
||||
/// NOTE: The 2nd order derivative of a 2d curve represents its curvature, so intuitively this function
|
||||
/// checks (as the name suggests) whether our approximation is _locally_ "flat". More curvy parts
|
||||
/// need to have a denser approximation to be more "flat".
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points to check for flatness.</param>
|
||||
/// <returns>Whether the control points are flat enough.</returns>
|
||||
private static bool IsFlatEnough(Vector2[] controlPoints)
|
||||
{
|
||||
for (int i = 1; i < controlPoints.Length - 1; i++)
|
||||
if ((controlPoints[i - 1] - 2 * controlPoints[i] + controlPoints[i + 1]).LengthSquared > TOLERANCE_SQ)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subdivides n control points representing a bezier curve into 2 sets of n control points, each
|
||||
/// describing a bezier curve equivalent to a half of the original curve. Effectively this splits
|
||||
/// the original curve into 2 curves which result in the original curve when pieced back together.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points to split.</param>
|
||||
/// <param name="l">Output: The control points corresponding to the left half of the curve.</param>
|
||||
/// <param name="r">Output: The control points corresponding to the right half of the curve.</param>
|
||||
private void Subdivide(Vector2[] controlPoints, Vector2[] l, Vector2[] r)
|
||||
{
|
||||
Vector2[] midpoints = subdivisionBuffer1;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
midpoints[i] = controlPoints[i];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
l[i] = midpoints[0];
|
||||
r[count - i - 1] = midpoints[count - i - 1];
|
||||
|
||||
for (int j = 0; j < count - i - 1; j++)
|
||||
midpoints[j] = (midpoints[j] + midpoints[j + 1]) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This uses <a href="https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm">De Casteljau's algorithm</a> to obtain an optimal
|
||||
/// piecewise-linear approximation of the bezier curve with the same amount of points as there are control points.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points describing the bezier curve to be approximated.</param>
|
||||
/// <param name="output">The points representing the resulting piecewise-linear approximation.</param>
|
||||
private void Approximate(Vector2[] controlPoints, List<Vector2> output)
|
||||
{
|
||||
Vector2[] l = subdivisionBuffer2;
|
||||
Vector2[] r = subdivisionBuffer1;
|
||||
|
||||
Subdivide(controlPoints, l, r);
|
||||
|
||||
for (int i = 0; i < count - 1; ++i)
|
||||
l[count + i] = r[i + 1];
|
||||
|
||||
output.Add(controlPoints[0]);
|
||||
for (int i = 1; i < count - 1; ++i)
|
||||
{
|
||||
int index = 2 * i;
|
||||
Vector2 p = 0.25f * (l[index - 1] + 2 * l[index] + l[index + 1]);
|
||||
output.Add(p);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a piecewise-linear approximation of a bezier curve, by adaptively repeatedly subdividing
|
||||
/// the control points until their approximation error vanishes below a given threshold.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points describing the curve.</param>
|
||||
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
||||
public List<Vector2> CreateBezier()
|
||||
{
|
||||
List<Vector2> output = new List<Vector2>();
|
||||
|
||||
if (count == 0)
|
||||
return output;
|
||||
|
||||
Stack<Vector2[]> toFlatten = new Stack<Vector2[]>();
|
||||
Stack<Vector2[]> freeBuffers = new Stack<Vector2[]>();
|
||||
|
||||
// "toFlatten" contains all the curves which are not yet approximated well enough.
|
||||
// We use a stack to emulate recursion without the risk of running into a stack overflow.
|
||||
// (More specifically, we iteratively and adaptively refine our curve with a
|
||||
// <a href="https://en.wikipedia.org/wiki/Depth-first_search">Depth-first search</a>
|
||||
// over the tree resulting from the subdivisions we make.)
|
||||
toFlatten.Push(controlPoints.ToArray());
|
||||
|
||||
Vector2[] leftChild = subdivisionBuffer2;
|
||||
|
||||
while (toFlatten.Count > 0)
|
||||
{
|
||||
Vector2[] parent = toFlatten.Pop();
|
||||
if (IsFlatEnough(parent))
|
||||
{
|
||||
// If the control points we currently operate on are sufficiently "flat", we use
|
||||
// an extension to De Casteljau's algorithm to obtain a piecewise-linear approximation
|
||||
// of the bezier curve represented by our control points, consisting of the same amount
|
||||
// of points as there are control points.
|
||||
Approximate(parent, output);
|
||||
freeBuffers.Push(parent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we do not yet have a sufficiently "flat" (in other words, detailed) approximation we keep
|
||||
// subdividing the curve we are currently operating on.
|
||||
Vector2[] rightChild = freeBuffers.Count > 0 ? freeBuffers.Pop() : new Vector2[count];
|
||||
Subdivide(parent, leftChild, rightChild);
|
||||
|
||||
// We re-use the buffer of the parent for one of the children, so that we save one allocation per iteration.
|
||||
for (int i = 0; i < count; ++i)
|
||||
parent[i] = leftChild[i];
|
||||
|
||||
toFlatten.Push(rightChild);
|
||||
toFlatten.Push(parent);
|
||||
}
|
||||
|
||||
output.Add(controlPoints[count - 1]);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CurveTypes
|
||||
@@ -201,5 +29,5 @@ namespace osu.Game.Modes.Osu.Objects
|
||||
Bezier,
|
||||
Linear,
|
||||
PerfectCurve
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects
|
||||
{
|
||||
public class SliderCurve
|
||||
{
|
||||
public double Length;
|
||||
|
||||
public List<Vector2> Path;
|
||||
|
||||
public CurveTypes CurveType;
|
||||
|
||||
private List<Vector2> calculatedPath;
|
||||
|
||||
private List<Vector2> calculateSubpath(List<Vector2> subpath)
|
||||
{
|
||||
switch (CurveType)
|
||||
{
|
||||
case CurveTypes.Linear:
|
||||
return subpath;
|
||||
default:
|
||||
return new BezierApproximator(subpath).CreateBezier();
|
||||
}
|
||||
}
|
||||
|
||||
public void Calculate()
|
||||
{
|
||||
calculatedPath = new List<Vector2>();
|
||||
|
||||
// Sliders may consist of various subpaths separated by two consecutive vertices
|
||||
// with the same position. The following loop parses these subpaths and computes
|
||||
// their shape independently, consecutively appending them to calculatedPath.
|
||||
List<Vector2> subpath = new List<Vector2>();
|
||||
for (int i = 0; i < Path.Count; ++i)
|
||||
{
|
||||
subpath.Add(Path[i]);
|
||||
if (i == Path.Count - 1 || Path[i] == Path[i + 1])
|
||||
{
|
||||
// If we already constructed a subpath previously, then the new subpath
|
||||
// will have as starting position the end position of the previous subpath.
|
||||
// Hence we can and should remove the previous endpoint to avoid a segment
|
||||
// with 0 length.
|
||||
if (calculatedPath.Count > 0)
|
||||
calculatedPath.RemoveAt(calculatedPath.Count - 1);
|
||||
|
||||
calculatedPath.AddRange(calculateSubpath(subpath));
|
||||
subpath.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress)
|
||||
{
|
||||
progress = MathHelper.Clamp(progress, 0, 1);
|
||||
|
||||
double index = progress * (calculatedPath.Count - 1);
|
||||
int flooredIndex = (int)index;
|
||||
|
||||
Vector2 pos = calculatedPath[flooredIndex];
|
||||
if (index != flooredIndex)
|
||||
pos += (calculatedPath[flooredIndex + 1] - pos) * (float)(index - flooredIndex);
|
||||
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ namespace osu.Game.Modes.Osu
|
||||
|
||||
public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser();
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor();
|
||||
|
||||
protected override PlayMode PlayMode => PlayMode.Osu;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Modes.Osu
|
||||
{
|
||||
class OsuScore : Score
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Modes.Objects.Drawables;
|
||||
using osu.Game.Modes.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Modes.Osu
|
||||
{
|
||||
class OsuScoreProcessor : ScoreProcessor
|
||||
{
|
||||
protected override void UpdateCalculations(JudgementInfo judgement)
|
||||
{
|
||||
if (judgement != null)
|
||||
{
|
||||
switch (judgement.Result)
|
||||
{
|
||||
case HitResult.Hit:
|
||||
Combo.Value++;
|
||||
break;
|
||||
case HitResult.Miss:
|
||||
Combo.Value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int score = 0;
|
||||
int maxScore = 0;
|
||||
|
||||
foreach (OsuJudgementInfo j in Judgements)
|
||||
{
|
||||
switch (j.Score)
|
||||
{
|
||||
case OsuScoreResult.Miss:
|
||||
maxScore += 300;
|
||||
break;
|
||||
case OsuScoreResult.Hit50:
|
||||
score += 50;
|
||||
maxScore += 300;
|
||||
break;
|
||||
case OsuScoreResult.Hit100:
|
||||
score += 100;
|
||||
maxScore += 300;
|
||||
break;
|
||||
case OsuScoreResult.Hit300:
|
||||
score += 300;
|
||||
maxScore += 300;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TotalScore.Value = score;
|
||||
Accuracy.Value = (double)score / maxScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,15 +37,6 @@ namespace osu.Game.Modes.Osu.UI
|
||||
|
||||
AddInternal(new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Color4.Black,
|
||||
Depth = float.MinValue,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
approachCircles = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Objects\BezierApproximator.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableSlider.cs" />
|
||||
@@ -52,6 +54,9 @@
|
||||
<Compile Include="Objects\Drawables\Pieces\RingPiece.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\Triangles.cs" />
|
||||
<Compile Include="Objects\OsuHitObjectParser.cs" />
|
||||
<Compile Include="Objects\SliderCurve.cs" />
|
||||
<Compile Include="OsuScore.cs" />
|
||||
<Compile Include="OsuScoreProcessor.cs" />
|
||||
<Compile Include="UI\OsuComboCounter.cs" />
|
||||
<Compile Include="UI\OsuHitRenderer.cs" />
|
||||
<Compile Include="UI\OsuPlayfield.cs" />
|
||||
|
||||
@@ -18,6 +18,8 @@ namespace osu.Game.Modes.Catch
|
||||
|
||||
protected override PlayMode PlayMode => PlayMode.Catch;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => null;
|
||||
|
||||
public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace osu.Game.Modes.Mania
|
||||
|
||||
protected override PlayMode PlayMode => PlayMode.Mania;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => null;
|
||||
|
||||
public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//Copyright (c) 2007-2016 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.Game.Modes.Objects;
|
||||
using osu.Game.Modes.Osu.Objects;
|
||||
@@ -18,6 +19,8 @@ namespace osu.Game.Modes.Taiko
|
||||
|
||||
protected override PlayMode PlayMode => PlayMode.Taiko;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => null;
|
||||
|
||||
public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,27 @@ namespace osu.Game.Beatmaps
|
||||
public List<HitObject> HitObjects { get; set; }
|
||||
public List<ControlPoint> ControlPoints { get; set; }
|
||||
public List<Color4> ComboColors { get; set; }
|
||||
|
||||
public double BeatLengthAt(double time, bool applyMultipliers = false)
|
||||
{
|
||||
int point = 0;
|
||||
int samplePoint = 0;
|
||||
|
||||
for (int i = 0; i < ControlPoints.Count; i++)
|
||||
if (ControlPoints[i].Time <= time)
|
||||
{
|
||||
if (ControlPoints[i].TimingChange)
|
||||
point = i;
|
||||
else
|
||||
samplePoint = i;
|
||||
}
|
||||
|
||||
double mult = 1;
|
||||
|
||||
if (applyMultipliers && samplePoint > point && ControlPoints[samplePoint].BeatLength < 0)
|
||||
mult = ControlPoints[samplePoint].VelocityAdjustment;
|
||||
|
||||
return ControlPoints[point].BeatLength * mult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//Copyright (c) 2007-2016 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.Sprites;
|
||||
|
||||
namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
class BeatmapBackgroundSprite : Sprite
|
||||
{
|
||||
private readonly WorkingBeatmap working;
|
||||
|
||||
public BeatmapBackgroundSprite(WorkingBeatmap working)
|
||||
{
|
||||
this.working = working;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game)
|
||||
{
|
||||
Texture = working.Background;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,14 +35,8 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
switch (state)
|
||||
{
|
||||
case BeatmapGroupState.Expanded:
|
||||
//if (!difficulties.Children.All(d => IsLoaded))
|
||||
// Task.WhenAll(difficulties.Children.Select(d => d.Preload(Game))).ContinueWith(t => difficulties.Show());
|
||||
//else
|
||||
foreach (BeatmapPanel panel in BeatmapPanels)
|
||||
{
|
||||
panel.Hidden = false;
|
||||
panel.FadeIn(250);
|
||||
}
|
||||
|
||||
Header.State = PanelSelectedState.Selected;
|
||||
if (SelectedPanel != null)
|
||||
@@ -54,11 +48,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
SelectedPanel.State = PanelSelectedState.NotSelected;
|
||||
|
||||
foreach (BeatmapPanel panel in BeatmapPanels)
|
||||
{
|
||||
panel.Hidden = true;
|
||||
panel.FadeOut(250);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -76,6 +66,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
|
||||
BeatmapPanels = beatmap.BeatmapSetInfo.Beatmaps.Select(b => new BeatmapPanel(b)
|
||||
{
|
||||
Alpha = 0,
|
||||
GainedSelection = panelGainedSelection,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
}).ToList();
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
//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.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@@ -22,6 +26,8 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
|
||||
public Action<BeatmapPanel> GainedSelection;
|
||||
|
||||
Color4 deselectedColour = new Color4(20, 43, 51, 255);
|
||||
|
||||
protected override void Selected()
|
||||
{
|
||||
base.Selected();
|
||||
@@ -36,7 +42,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
base.Deselected();
|
||||
|
||||
background.Colour = new Color4(20, 43, 51, 255);
|
||||
background.Colour = deselectedColour;
|
||||
}
|
||||
|
||||
public BeatmapPanel(BeatmapInfo beatmap)
|
||||
@@ -44,12 +50,22 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
Beatmap = beatmap;
|
||||
Height *= 0.60f;
|
||||
|
||||
Children = new Framework.Graphics.Drawable[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Triangles
|
||||
{
|
||||
// The border is drawn in the shader of the children. Being additive, triangles would over-emphasize
|
||||
// the border wherever they cross it, and thus they get their own masking container without a border.
|
||||
Masking = true,
|
||||
CornerRadius = Content.CornerRadius,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Colour = deselectedColour,
|
||||
},
|
||||
new FlowContainer
|
||||
{
|
||||
Padding = new MarginPadding(5),
|
||||
@@ -57,7 +73,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Children = new Framework.Graphics.Drawable[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DifficultyIcon(FontAwesome.fa_dot_circle_o, new Color4(159, 198, 0, 255))
|
||||
{
|
||||
@@ -71,7 +87,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
Spacing = new Vector2(0, 5),
|
||||
Direction = FlowDirection.VerticalOnly,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Framework.Graphics.Drawable[]
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FlowContainer
|
||||
{
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
public BeatmapSetHeader(WorkingBeatmap beatmap)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
Hidden = false;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@@ -72,6 +71,12 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
FadeInFromZero(250);
|
||||
}
|
||||
|
||||
protected override void Selected()
|
||||
{
|
||||
base.Selected();
|
||||
@@ -114,7 +119,7 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
Children = new[]
|
||||
{
|
||||
new FlowContainer
|
||||
{
|
||||
{
|
||||
Depth = -1,
|
||||
Direction = FlowDirection.HorizontalOnly,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -156,38 +161,17 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game)
|
||||
{
|
||||
{
|
||||
new BeatmapBackgroundSprite(working)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill,
|
||||
}.Preload(game, (bg) =>
|
||||
{
|
||||
Add(bg);
|
||||
ForceRedraw();
|
||||
});
|
||||
}
|
||||
|
||||
class BeatmapBackground : Sprite
|
||||
{
|
||||
private readonly WorkingBeatmap working;
|
||||
|
||||
public BeatmapBackground(WorkingBeatmap working)
|
||||
{
|
||||
this.working = working;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game)
|
||||
{
|
||||
Texture = working.Background;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Scale = new Vector2(1366 / (Texture?.Width ?? 1) * 0.6f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
@@ -14,7 +15,12 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
public const float MAX_HEIGHT = 80;
|
||||
|
||||
public bool Hidden = true;
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public bool IsOnScreen;
|
||||
|
||||
public override bool IsAlive => IsOnScreen && base.IsAlive;
|
||||
|
||||
private Container nestedContainer;
|
||||
|
||||
protected override Container<Drawable> Content => nestedContainer;
|
||||
@@ -42,7 +48,6 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
{
|
||||
base.LoadComplete();
|
||||
applyState();
|
||||
FadeInFromZero(250);
|
||||
}
|
||||
|
||||
private void applyState()
|
||||
|
||||
@@ -187,7 +187,25 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private void handleTimingPoints(Beatmap beatmap, string val)
|
||||
{
|
||||
// TODO
|
||||
ControlPoint cp = null;
|
||||
|
||||
string[] split = val.Split(',');
|
||||
|
||||
if (split.Length > 2)
|
||||
{
|
||||
int kiai_flags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0;
|
||||
double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
|
||||
cp = new ControlPoint
|
||||
{
|
||||
Time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo),
|
||||
BeatLength = beatLength > 0 ? beatLength : 0,
|
||||
VelocityAdjustment = beatLength < 0 ? -beatLength / 100.0 : 1,
|
||||
TimingChange = split.Length <= 6 || split[6][0] == '1',
|
||||
};
|
||||
}
|
||||
|
||||
if (cp != null)
|
||||
beatmap.ControlPoints.Add(cp);
|
||||
}
|
||||
|
||||
private void handleColours(Beatmap beatmap, string key, string val)
|
||||
@@ -275,8 +293,12 @@ namespace osu.Game.Beatmaps.Formats
|
||||
break;
|
||||
case Section.HitObjects:
|
||||
var obj = parser?.Parse(val);
|
||||
|
||||
if (obj != null)
|
||||
{
|
||||
obj.SetDefaultsFromBeatmap(beatmap);
|
||||
beatmap.HitObjects.Add(obj);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,5 +12,14 @@ namespace osu.Game.Beatmaps.Timing
|
||||
public class ControlPoint
|
||||
{
|
||||
public double Time;
|
||||
public double BeatLength;
|
||||
public double VelocityAdjustment;
|
||||
public bool TimingChange;
|
||||
}
|
||||
|
||||
internal enum TimeSignatures
|
||||
{
|
||||
SimpleQuadruple = 4,
|
||||
SimpleTriple = 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace osu.Game.Configuration
|
||||
Set(OsuConfig.AudioDevice, string.Empty);
|
||||
//Set(OsuConfig.ReleaseStream, ReleaseStream.Lazer, true);
|
||||
Set(OsuConfig.UpdateFailCount, 0);
|
||||
//Set(OsuConfig.SavePassword, Password != null);
|
||||
Set(OsuConfig.SavePassword, false);
|
||||
Set(OsuConfig.SaveUsername, true);
|
||||
//Set(OsuConfig.TreeSortMode, TreeGroupMode.Show_All);
|
||||
//Set(OsuConfig.TreeSortMode2, TreeSortMode.Title);
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
this.textureName = textureName;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Depth = float.MinValue;
|
||||
Depth = float.MaxValue;
|
||||
|
||||
Add(Sprite = new Sprite
|
||||
{
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.MathUtils;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
public class Triangles : Container<Triangle>
|
||||
{
|
||||
public Triangles()
|
||||
{
|
||||
Alpha = 0.3f;
|
||||
}
|
||||
|
||||
private float triangleScale = 1;
|
||||
|
||||
public float TriangleScale
|
||||
{
|
||||
get { return triangleScale; }
|
||||
set
|
||||
{
|
||||
triangleScale = value;
|
||||
|
||||
Children.ForEach(t => t.ScaleTo(triangleScale));
|
||||
}
|
||||
}
|
||||
|
||||
private int aimTriangleCount => (int)((DrawWidth * DrawHeight) / 800 / triangleScale);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
foreach (Drawable d in Children)
|
||||
{
|
||||
d.Position -= new Vector2(0, (float)(d.Scale.X * (50 / DrawHeight) * (Time.Elapsed / 880)) / triangleScale);
|
||||
if (d.DrawPosition.Y + d.DrawSize.Y * d.Scale.Y < 0)
|
||||
d.Expire();
|
||||
}
|
||||
|
||||
bool useRandomX = Children.Count() < aimTriangleCount / 2;
|
||||
while (Children.Count() < aimTriangleCount)
|
||||
addTriangle(useRandomX);
|
||||
|
||||
}
|
||||
|
||||
protected virtual Triangle CreateTriangle()
|
||||
{
|
||||
var scale = triangleScale * RNG.NextSingle() * 0.4f + 0.2f;
|
||||
const float size = 100;
|
||||
|
||||
return new Triangle
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Scale = new Vector2(scale),
|
||||
// Scaling height by 0.866 results in equiangular triangles (== 60° and equal side length)
|
||||
Size = new Vector2(size, 0.866f * size),
|
||||
Alpha = RNG.NextSingle(),
|
||||
Depth = scale,
|
||||
};
|
||||
}
|
||||
|
||||
private void addTriangle(bool randomX)
|
||||
{
|
||||
var sprite = CreateTriangle();
|
||||
sprite.Position = new Vector2(RNG.NextSingle(), randomX ? RNG.NextSingle() : 1);
|
||||
Add(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
@@ -35,11 +36,16 @@ namespace osu.Game.Graphics.Containers
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
bool firstUpdate = true;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
content.Position = (ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount;
|
||||
|
||||
content.MoveTo((ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount, firstUpdate ? 0 : 1000, EasingTypes.OutQuint);
|
||||
content.Scale = new Vector2(1 + ParallaxAmount);
|
||||
|
||||
firstUpdate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
//Copyright (c) 2007-2016 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.Framework.Graphics.Shaders;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using System;
|
||||
using osu.Framework.Graphics.OpenGL;
|
||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
|
||||
namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
|
||||
class CursorTrail : Drawable
|
||||
{
|
||||
public override bool Contains(Vector2 screenSpacePos) => true;
|
||||
public override bool HandleInput => true;
|
||||
|
||||
private int currentIndex;
|
||||
|
||||
private Shader shader;
|
||||
private Texture texture;
|
||||
|
||||
private Vector2 size => texture.Size * Scale;
|
||||
|
||||
private double timeOffset;
|
||||
|
||||
private float time;
|
||||
|
||||
private TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
|
||||
private const int MAX_SPRITES = 2048;
|
||||
|
||||
private TrailPart[] parts = new TrailPart[MAX_SPRITES];
|
||||
|
||||
private Vector2? lastPosition;
|
||||
|
||||
protected override DrawNode CreateDrawNode() => new TrailDrawNode();
|
||||
|
||||
protected override void ApplyDrawNode(DrawNode node)
|
||||
{
|
||||
base.ApplyDrawNode(node);
|
||||
|
||||
TrailDrawNode tNode = node as TrailDrawNode;
|
||||
tNode.Shader = shader;
|
||||
tNode.Texture = texture;
|
||||
tNode.Size = size;
|
||||
tNode.Time = time;
|
||||
tNode.Shared = trailDrawNodeSharedData;
|
||||
|
||||
for (int i = 0; i < parts.Length; ++i)
|
||||
if (parts[i].InvalidationID > tNode.Parts[i].InvalidationID)
|
||||
tNode.Parts[i] = parts[i];
|
||||
}
|
||||
|
||||
public CursorTrail()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
for (int i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
parts[i].InvalidationID = 0;
|
||||
parts[i].WasUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ShaderManager shaders, TextureStore textures)
|
||||
{
|
||||
shader = shaders?.Load(@"CursorTrail", FragmentShaderDescriptor.Texture);
|
||||
texture = textures.Get(@"Cursor/cursortrail");
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Invalidate(Invalidation.DrawNode, shallPropagate: false);
|
||||
|
||||
int fadeClockResetThreshold = 1000000;
|
||||
|
||||
time = (float)(Time.Current - timeOffset) / 500f;
|
||||
if (time > fadeClockResetThreshold)
|
||||
ResetTime();
|
||||
}
|
||||
|
||||
private void ResetTime()
|
||||
{
|
||||
for (int i = 0; i < parts.Length; ++i)
|
||||
{
|
||||
parts[i].Time -= time;
|
||||
++parts[i].InvalidationID;
|
||||
}
|
||||
|
||||
time = 0;
|
||||
timeOffset = Time.Current;
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
if (lastPosition == null)
|
||||
{
|
||||
lastPosition = state.Mouse.NativeState.Position;
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
Vector2 pos1 = lastPosition.Value;
|
||||
Vector2 pos2 = state.Mouse.NativeState.Position;
|
||||
|
||||
Vector2 diff = pos2 - pos1;
|
||||
float distance = diff.Length;
|
||||
Vector2 direction = diff / distance;
|
||||
|
||||
float interval = (size.X / 2) * 0.9f;
|
||||
|
||||
for (float d = interval; d < distance; d += interval)
|
||||
{
|
||||
lastPosition = pos1 + direction * d;
|
||||
addPosition(lastPosition.Value);
|
||||
}
|
||||
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
private void addPosition(Vector2 pos)
|
||||
{
|
||||
parts[currentIndex].Position = pos;
|
||||
parts[currentIndex].Time = time;
|
||||
++parts[currentIndex].InvalidationID;
|
||||
|
||||
currentIndex = (currentIndex + 1) % MAX_SPRITES;
|
||||
}
|
||||
|
||||
struct TrailPart
|
||||
{
|
||||
public Vector2 Position;
|
||||
public float Time;
|
||||
public long InvalidationID;
|
||||
public bool WasUpdated;
|
||||
}
|
||||
|
||||
class TrailDrawNodeSharedData
|
||||
{
|
||||
public VertexBuffer<TexturedVertex2D> VertexBuffer;
|
||||
}
|
||||
|
||||
class TrailDrawNode : DrawNode
|
||||
{
|
||||
public Shader Shader;
|
||||
public Texture Texture;
|
||||
|
||||
public float Time;
|
||||
public TrailDrawNodeSharedData Shared;
|
||||
|
||||
public TrailPart[] Parts = new TrailPart[MAX_SPRITES];
|
||||
public Vector2 Size;
|
||||
|
||||
public TrailDrawNode()
|
||||
{
|
||||
for (int i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
Parts[i].InvalidationID = 0;
|
||||
Parts[i].WasUpdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Draw(Action<TexturedVertex2D> vertexAction)
|
||||
{
|
||||
if (Shared.VertexBuffer == null)
|
||||
Shared.VertexBuffer = new QuadVertexBuffer<TexturedVertex2D>(MAX_SPRITES, BufferUsageHint.DynamicDraw);
|
||||
|
||||
Shader.GetUniform<float>("g_FadeClock").Value = Time;
|
||||
|
||||
int updateStart = -1, updateEnd = 0;
|
||||
for (int i = 0; i < Parts.Length; ++i)
|
||||
{
|
||||
if (Parts[i].WasUpdated)
|
||||
{
|
||||
if (updateStart == -1)
|
||||
updateStart = i;
|
||||
updateEnd = i + 1;
|
||||
|
||||
int start = i * 4;
|
||||
int end = start;
|
||||
|
||||
Vector2 pos = Parts[i].Position;
|
||||
ColourInfo colour = DrawInfo.Colour;
|
||||
colour.TopLeft.Linear.A = Parts[i].Time + colour.TopLeft.Linear.A;
|
||||
colour.TopRight.Linear.A = Parts[i].Time + colour.TopRight.Linear.A;
|
||||
colour.BottomLeft.Linear.A = Parts[i].Time + colour.BottomLeft.Linear.A;
|
||||
colour.BottomRight.Linear.A = Parts[i].Time + colour.BottomRight.Linear.A;
|
||||
|
||||
Texture.DrawQuad(
|
||||
new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
|
||||
colour,
|
||||
null,
|
||||
v => Shared.VertexBuffer.Vertices[end++] = v);
|
||||
|
||||
Parts[i].WasUpdated = false;
|
||||
}
|
||||
else if (updateStart != -1)
|
||||
{
|
||||
Shared.VertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4);
|
||||
updateStart = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Update all remaining vertices that have been changed.
|
||||
if (updateStart != -1)
|
||||
Shared.VertexBuffer.UpdateRange(updateStart * 4, updateEnd * 4);
|
||||
|
||||
base.Draw(vertexAction);
|
||||
|
||||
Shader.Bind();
|
||||
|
||||
Texture.TextureGL.Bind();
|
||||
Shared.VertexBuffer.Draw();
|
||||
|
||||
Shader.Unbind();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -17,9 +17,14 @@ namespace osu.Game.Graphics.Cursor
|
||||
{
|
||||
protected override Drawable CreateCursor() => new OsuCursor();
|
||||
|
||||
public OsuCursorContainer()
|
||||
{
|
||||
Add(new CursorTrail { Depth = 1 });
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
ActiveCursor.Scale = new OpenTK.Vector2(1);
|
||||
ActiveCursor.Scale = new Vector2(1);
|
||||
ActiveCursor.ScaleTo(1.2f, 100, EasingTypes.OutQuad);
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
@@ -52,5 +57,4 @@ namespace osu.Game.Graphics.Cursor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
// Basic back button as it was on stable (kinda). No skinning possible for now
|
||||
public class BackButton : ClickableContainer
|
||||
{
|
||||
private TextAwesome icon;
|
||||
|
||||
private Container leftContainer;
|
||||
private Container rightContainer;
|
||||
|
||||
private Box leftBox;
|
||||
private Box rightBox;
|
||||
|
||||
private const double transform_time = 300.0;
|
||||
private const int pulse_length = 250;
|
||||
|
||||
private const float shear = 0.1f;
|
||||
|
||||
private static readonly Vector2 size_extended = new Vector2(140, 50);
|
||||
private static readonly Vector2 size_retracted = new Vector2(100, 50);
|
||||
|
||||
public BackButton()
|
||||
{
|
||||
Size = size_retracted;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
leftContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.4f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
leftBox = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(195, 40, 140, 255),
|
||||
Shear = new Vector2(shear, 0),
|
||||
},
|
||||
icon = new TextAwesome
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
TextSize = 25,
|
||||
Icon = FontAwesome.fa_osu_left_o
|
||||
},
|
||||
}
|
||||
},
|
||||
rightContainer = new Container
|
||||
{
|
||||
Origin = Anchor.TopRight,
|
||||
Anchor = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.6f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
rightBox = new Box
|
||||
{
|
||||
Colour = new Color4(238, 51, 153, 255),
|
||||
Origin = Anchor.TopLeft,
|
||||
Anchor = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Shear = new Vector2(shear, 0),
|
||||
EdgeSmoothness = new Vector2(1.5f, 0),
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Text = @"Back",
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Contains(Vector2 screenSpacePos)
|
||||
{
|
||||
return leftBox.Contains(screenSpacePos) || rightBox.Contains(screenSpacePos);
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
icon.ClearTransformations();
|
||||
|
||||
ResizeTo(size_extended, transform_time, EasingTypes.OutElastic);
|
||||
|
||||
int duration = 0; //(int)(Game.Audio.BeatLength / 2);
|
||||
if (duration == 0) duration = pulse_length;
|
||||
|
||||
double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
|
||||
double startTime = Time.Current + offset;
|
||||
|
||||
// basic pulse
|
||||
icon.Transforms.Add(new TransformScale
|
||||
{
|
||||
StartValue = new Vector2(1.1f),
|
||||
EndValue = Vector2.One,
|
||||
StartTime = startTime,
|
||||
EndTime = startTime + duration,
|
||||
Easing = EasingTypes.Out,
|
||||
LoopCount = -1,
|
||||
LoopDelay = duration
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
icon.ClearTransformations();
|
||||
|
||||
ResizeTo(size_retracted, transform_time, EasingTypes.OutElastic);
|
||||
|
||||
int duration = 0; //(int)(Game.Audio.BeatLength);
|
||||
if (duration == 0) duration = pulse_length * 2;
|
||||
|
||||
double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
|
||||
double startTime = Time.Current + offset;
|
||||
|
||||
// slow pulse
|
||||
icon.Transforms.Add(new TransformScale
|
||||
{
|
||||
StartValue = new Vector2(1.1f),
|
||||
EndValue = Vector2.One,
|
||||
StartTime = startTime,
|
||||
EndTime = startTime + duration,
|
||||
Easing = EasingTypes.Out,
|
||||
LoopCount = -1,
|
||||
LoopDelay = duration
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
var flash = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Shear = new Vector2(shear, 0),
|
||||
Colour = new Color4(255, 255, 255, 128),
|
||||
};
|
||||
Add(flash);
|
||||
|
||||
flash.FadeOutFromOne(200);
|
||||
flash.Expire();
|
||||
|
||||
return base.OnClick(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,14 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Threading;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface.Volume
|
||||
{
|
||||
internal class VolumeControl : OverlayContainer
|
||||
{
|
||||
public BindableDouble VolumeGlobal { get; set; }
|
||||
public BindableDouble VolumeSample { get; set; }
|
||||
public BindableDouble VolumeTrack { get; set; }
|
||||
|
||||
private VolumeMeter volumeMeterMaster;
|
||||
|
||||
private void volumeChanged(object sender, EventArgs e)
|
||||
@@ -54,21 +51,18 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
VolumeGlobal.ValueChanged += volumeChanged;
|
||||
VolumeSample.ValueChanged += volumeChanged;
|
||||
VolumeTrack.ValueChanged += volumeChanged;
|
||||
|
||||
volumeMeterMaster.Bindable = VolumeGlobal;
|
||||
volumeMeterEffect.Bindable = VolumeSample;
|
||||
volumeMeterMusic.Bindable = VolumeTrack;
|
||||
volumeMeterMaster.Bindable.ValueChanged += volumeChanged;
|
||||
volumeMeterEffect.Bindable.ValueChanged += volumeChanged;
|
||||
volumeMeterMusic.Bindable.ValueChanged += volumeChanged;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
VolumeGlobal.ValueChanged -= volumeChanged;
|
||||
VolumeSample.ValueChanged -= volumeChanged;
|
||||
VolumeTrack.ValueChanged -= volumeChanged;
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
volumeMeterMaster.Bindable.ValueChanged -= volumeChanged;
|
||||
volumeMeterEffect.Bindable.ValueChanged -= volumeChanged;
|
||||
volumeMeterMusic.Bindable.ValueChanged -= volumeChanged;
|
||||
}
|
||||
|
||||
public void Adjust(InputState state)
|
||||
@@ -82,6 +76,14 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
volumeMeterMaster.TriggerWheel(state);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
volumeMeterMaster.Bindable.Weld(audio.Volume);
|
||||
volumeMeterEffect.Bindable.Weld(audio.VolumeSample);
|
||||
volumeMeterMusic.Bindable.Weld(audio.VolumeTrack);
|
||||
}
|
||||
|
||||
ScheduledDelegate popOutDelegate;
|
||||
|
||||
private VolumeMeter volumeMeterEffect;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
internal class VolumeMeter : Container
|
||||
{
|
||||
private Box meterFill;
|
||||
public BindableDouble Bindable;
|
||||
public BindableDouble Bindable { get; private set; } = new BindableDouble();
|
||||
|
||||
public VolumeMeter(string meterName)
|
||||
{
|
||||
@@ -41,6 +41,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
meterFill = new Box
|
||||
{
|
||||
Colour = Color4.White,
|
||||
Scale = new Vector2(1, 0),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre
|
||||
@@ -54,6 +55,8 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
Origin = Anchor.TopCentre
|
||||
}
|
||||
};
|
||||
|
||||
Bindable.ValueChanged += delegate { updateFill(); };
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -68,7 +71,6 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
private set
|
||||
{
|
||||
Bindable.Value = value;
|
||||
updateFill();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
//Copyright (c) 2007-2016 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.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Game.Modes.Objects;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes
|
||||
{
|
||||
public class HitJudgementResolver
|
||||
{
|
||||
public JudgementResult CheckJudgement(HitObject h) => new JudgementResult { Combo = ComboJudgement.None, Judgement = Judgement.Hit300 };
|
||||
}
|
||||
|
||||
public struct JudgementResult
|
||||
{
|
||||
public ComboJudgement Combo;
|
||||
public Judgement Judgement;
|
||||
public float TimeOffset;
|
||||
public Vector2 PositionOffset;
|
||||
}
|
||||
|
||||
public enum ComboJudgement
|
||||
{
|
||||
[Description(@"")]
|
||||
None,
|
||||
[Description(@"Good")]
|
||||
Good,
|
||||
[Description(@"Amazing")]
|
||||
Perfect
|
||||
}
|
||||
|
||||
public enum Judgement
|
||||
{
|
||||
[Description(@"Miss")]
|
||||
Miss,
|
||||
[Description(@"50")]
|
||||
Hit50,
|
||||
[Description(@"100")]
|
||||
Hit100,
|
||||
[Description(@"300")]
|
||||
Hit300,
|
||||
[Description(@"500")]
|
||||
Hit500
|
||||
}
|
||||
}
|
||||
@@ -2,29 +2,31 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableHitObject : Container, IStateful<ArmedState>
|
||||
{
|
||||
//todo: move to a more central implementation. this logic should not be at a drawable level.
|
||||
public Action<DrawableHitObject> OnHit;
|
||||
public Action<DrawableHitObject> OnMiss;
|
||||
|
||||
public Func<DrawableHitObject, bool> AllowHit;
|
||||
public event Action<DrawableHitObject, JudgementInfo> OnJudgement;
|
||||
|
||||
public Container<DrawableHitObject> ChildObjects;
|
||||
|
||||
public JudgementResult Result;
|
||||
public JudgementInfo Judgement;
|
||||
|
||||
public abstract JudgementInfo CreateJudgementInfo();
|
||||
|
||||
public HitObject HitObject;
|
||||
|
||||
public DrawableHitObject(HitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
Depth = -(float)hitObject.StartTime;
|
||||
Depth = (float)hitObject.StartTime;
|
||||
}
|
||||
|
||||
private ArmedState state;
|
||||
@@ -41,36 +43,55 @@ namespace osu.Game.Modes.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected double? HitTime;
|
||||
|
||||
protected virtual bool Hit()
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
if (State != ArmedState.Disarmed)
|
||||
base.LoadComplete();
|
||||
|
||||
Judgement = CreateJudgementInfo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a hit of this hitobject. Carries out judgement.
|
||||
/// </summary>
|
||||
/// <param name="judgement">Preliminary judgement information provided by the hit source.</param>
|
||||
/// <returns>Whether a hit was processed.</returns>
|
||||
protected bool UpdateJudgement(bool userTriggered)
|
||||
{
|
||||
if (Judgement.Result != null)
|
||||
return false;
|
||||
|
||||
if (AllowHit?.Invoke(this) == false)
|
||||
Judgement.TimeOffset = Time.Current - HitObject.EndTime;
|
||||
|
||||
CheckJudgement(userTriggered);
|
||||
|
||||
if (Judgement.Result == null)
|
||||
return false;
|
||||
|
||||
HitTime = Time.Current;
|
||||
switch (Judgement.Result)
|
||||
{
|
||||
default:
|
||||
State = ArmedState.Hit;
|
||||
break;
|
||||
case HitResult.Miss:
|
||||
State = ArmedState.Miss;
|
||||
break;
|
||||
}
|
||||
|
||||
OnJudgement?.Invoke(this, Judgement);
|
||||
|
||||
State = ArmedState.Armed;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool counted;
|
||||
protected virtual void CheckJudgement(bool userTriggered)
|
||||
{
|
||||
//todo: consider making abstract.
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Time.Current >= HitObject.EndTime && !counted)
|
||||
{
|
||||
counted = true;
|
||||
if (state == ArmedState.Armed)
|
||||
OnHit?.Invoke(this);
|
||||
else
|
||||
OnMiss?.Invoke(this);
|
||||
}
|
||||
UpdateJudgement(false);
|
||||
}
|
||||
|
||||
protected abstract void UpdateState(ArmedState state);
|
||||
@@ -78,7 +99,28 @@ namespace osu.Game.Modes.Objects.Drawables
|
||||
|
||||
public enum ArmedState
|
||||
{
|
||||
Disarmed,
|
||||
Armed
|
||||
Idle,
|
||||
Hit,
|
||||
Miss
|
||||
}
|
||||
|
||||
public class PositionalJudgementInfo : JudgementInfo
|
||||
{
|
||||
public Vector2 PositionOffset;
|
||||
}
|
||||
|
||||
public class JudgementInfo
|
||||
{
|
||||
public ulong? ComboAtHit;
|
||||
public HitResult? Result;
|
||||
public double TimeOffset;
|
||||
}
|
||||
|
||||
public enum HitResult
|
||||
{
|
||||
[Description(@"Miss")]
|
||||
Miss,
|
||||
[Description(@"Hit")]
|
||||
Hit,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Samples;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@@ -21,5 +22,7 @@ namespace osu.Game.Modes.Objects
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public HitSampleInfo Sample;
|
||||
|
||||
public virtual void SetDefaultsFromBeatmap(Beatmap beatmap) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ namespace osu.Game.Modes
|
||||
|
||||
public abstract ScoreOverlay CreateScoreOverlay();
|
||||
|
||||
public abstract ScoreProcessor CreateScoreProcessor();
|
||||
|
||||
public abstract HitRenderer CreateHitRendererWith(List<HitObject> objects);
|
||||
|
||||
public abstract HitObjectParser CreateHitObjectParser();
|
||||
|
||||
public virtual HitJudgementResolver CreateHitJudgement() => new HitJudgementResolver();
|
||||
|
||||
public static void Register(Ruleset ruleset) => availableRulesets.TryAdd(ruleset.PlayMode, ruleset.GetType());
|
||||
|
||||
protected abstract PlayMode PlayMode { get; }
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//Copyright (c) 2007-2016 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 System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Modes
|
||||
{
|
||||
public class Score
|
||||
{
|
||||
public double TotalScore { get; set; }
|
||||
public double Accuracy { get; set; }
|
||||
public double Combo { get; set; }
|
||||
public double MaxCombo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//Copyright (c) 2007-2016 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 System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Modes.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Modes
|
||||
{
|
||||
public abstract class ScoreProcessor
|
||||
{
|
||||
public virtual Score GetScore() => new Score()
|
||||
{
|
||||
TotalScore = TotalScore,
|
||||
Combo = Combo,
|
||||
MaxCombo = HighestCombo,
|
||||
Accuracy = Accuracy
|
||||
};
|
||||
|
||||
public readonly BindableDouble TotalScore = new BindableDouble { MinValue = 0 };
|
||||
|
||||
public readonly BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 };
|
||||
|
||||
public readonly BindableInt Combo = new BindableInt();
|
||||
|
||||
public readonly BindableInt HighestCombo = new BindableInt();
|
||||
|
||||
public readonly List<JudgementInfo> Judgements = new List<JudgementInfo>();
|
||||
|
||||
public ScoreProcessor()
|
||||
{
|
||||
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
|
||||
}
|
||||
|
||||
public void AddJudgement(JudgementInfo judgement)
|
||||
{
|
||||
Judgements.Add(judgement);
|
||||
|
||||
UpdateCalculations(judgement);
|
||||
|
||||
judgement.ComboAtHit = (ulong)Combo.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update any values that potentially need post-processing on a judgement change.
|
||||
/// </summary>
|
||||
/// <param name="newJudgement">A new JudgementInfo that triggered this calculation. May be null.</param>
|
||||
protected abstract void UpdateCalculations(JudgementInfo newJudgement);
|
||||
}
|
||||
}
|
||||
@@ -262,5 +262,13 @@ namespace osu.Game.Modes.UI
|
||||
(d as ComboCounter).DisplayedCount = CurrentValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void Set(ulong value)
|
||||
{
|
||||
if (value == 0)
|
||||
Roll();
|
||||
else
|
||||
Count = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,21 @@ namespace osu.Game.Modes.UI
|
||||
{
|
||||
public abstract class HitRenderer : Container
|
||||
{
|
||||
public Action<HitObject> OnHit;
|
||||
public Action<HitObject> OnMiss;
|
||||
public event Action<JudgementInfo> OnJudgement;
|
||||
|
||||
public event Action OnAllJudged;
|
||||
|
||||
protected void TriggerOnJudgement(JudgementInfo j)
|
||||
{
|
||||
OnJudgement?.Invoke(j);
|
||||
if (AllObjectsJudged)
|
||||
OnAllJudged?.Invoke();
|
||||
}
|
||||
|
||||
protected Playfield Playfield;
|
||||
|
||||
public bool AllObjectsJudged => Playfield.HitObjects.Children.First()?.Judgement.Result != null; //reverse depth sort means First() instead of Last().
|
||||
|
||||
public IEnumerable<DrawableHitObject> DrawableObjects => Playfield.HitObjects.Children;
|
||||
}
|
||||
|
||||
@@ -68,22 +78,13 @@ namespace osu.Game.Modes.UI
|
||||
|
||||
if (drawableObject == null) continue;
|
||||
|
||||
drawableObject.OnHit = onHit;
|
||||
drawableObject.OnMiss = onMiss;
|
||||
drawableObject.OnJudgement += onJudgement;
|
||||
|
||||
Playfield.Add(drawableObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMiss(DrawableHitObject obj)
|
||||
{
|
||||
OnMiss?.Invoke(obj.HitObject);
|
||||
}
|
||||
|
||||
private void onHit(DrawableHitObject obj)
|
||||
{
|
||||
OnHit?.Invoke(obj.HitObject);
|
||||
}
|
||||
private void onJudgement(DrawableHitObject o, JudgementInfo j) => TriggerOnJudgement(j);
|
||||
|
||||
protected abstract DrawableHitObject GetVisualRepresentation(T h);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace osu.Game.Modes.UI
|
||||
|
||||
public virtual void Add(DrawableHitObject h) => HitObjects.Add(h);
|
||||
|
||||
public override bool Contains(Vector2 screenSpacePos) => true;
|
||||
|
||||
public Playfield()
|
||||
{
|
||||
AddInternal(HitObjects = new HitObjectContainer
|
||||
@@ -25,6 +27,8 @@ namespace osu.Game.Modes.UI
|
||||
public class HitObjectContainer : Container<DrawableHitObject>
|
||||
{
|
||||
protected override Vector2 DrawScale => new Vector2(DrawSize.X / 512);
|
||||
|
||||
public override bool Contains(Vector2 screenSpacePos) => true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace osu.Game.Modes.UI
|
||||
public ComboCounter ComboCounter;
|
||||
public ScoreCounter ScoreCounter;
|
||||
public PercentageCounter AccuracyCounter;
|
||||
public Score Score { get; set; }
|
||||
|
||||
protected abstract KeyCounterCollection CreateKeyCounter();
|
||||
protected abstract ComboCounter CreateComboCounter();
|
||||
@@ -45,5 +46,13 @@ namespace osu.Game.Modes.UI
|
||||
AccuracyCounter = CreateAccuracyCounter(),
|
||||
};
|
||||
}
|
||||
|
||||
public void BindProcessor(ScoreProcessor processor)
|
||||
{
|
||||
//bind processor bindables to combocounter, score display etc.
|
||||
processor.TotalScore.ValueChanged += delegate { ScoreCounter?.Set((ulong)processor.TotalScore.Value); };
|
||||
processor.Accuracy.ValueChanged += delegate { AccuracyCounter?.Set((float)processor.Accuracy.Value); };
|
||||
processor.Combo.ValueChanged += delegate { ComboCounter?.Set((ulong)processor.Combo.Value); };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Online.API.Requests;
|
||||
@@ -17,8 +20,8 @@ namespace osu.Game.Online.API
|
||||
private OAuth authentication;
|
||||
|
||||
public string Endpoint = @"https://new.ppy.sh";
|
||||
const string ClientId = @"daNBnfdv7SppRVc61z0XuOI13y6Hroiz";
|
||||
const string ClientSecret = @"d6fgZuZeQ0eSXkEj5igdqQX6ztdtS6Ow";
|
||||
const string ClientId = @"5";
|
||||
const string ClientSecret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
|
||||
|
||||
ConcurrentQueue<APIRequest> queue = new ConcurrentQueue<APIRequest>();
|
||||
|
||||
@@ -26,15 +29,11 @@ namespace osu.Game.Online.API
|
||||
|
||||
public string Username;
|
||||
|
||||
private SecurePassword password;
|
||||
//private SecurePassword password;
|
||||
|
||||
public string Password
|
||||
{
|
||||
set
|
||||
{
|
||||
password = string.IsNullOrEmpty(value) ? null : new SecurePassword(value);
|
||||
}
|
||||
}
|
||||
public string Password;
|
||||
|
||||
public Bindable<User> LocalUser = new Bindable<User>();
|
||||
|
||||
public string Token
|
||||
{
|
||||
@@ -50,7 +49,7 @@ namespace osu.Game.Online.API
|
||||
}
|
||||
}
|
||||
|
||||
protected bool HasLogin => Token != null || (!string.IsNullOrEmpty(Username) && password != null);
|
||||
protected bool HasLogin => Token != null || (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password));
|
||||
|
||||
private Thread thread;
|
||||
|
||||
@@ -65,6 +64,25 @@ namespace osu.Game.Online.API
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
private List<IOnlineComponent> components = new List<IOnlineComponent>();
|
||||
|
||||
public void Register(IOnlineComponent component)
|
||||
{
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
components.Add(component);
|
||||
component.APIStateChanged(this, state);
|
||||
});
|
||||
}
|
||||
|
||||
public void Unregister(IOnlineComponent component)
|
||||
{
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
components.Remove(component);
|
||||
});
|
||||
}
|
||||
|
||||
public string AccessToken => authentication.RequestAccessToken();
|
||||
|
||||
/// <summary>
|
||||
@@ -102,7 +120,7 @@ namespace osu.Game.Online.API
|
||||
if (State < APIState.Connecting)
|
||||
State = APIState.Connecting;
|
||||
|
||||
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, password.Get(Representation.Raw)))
|
||||
if (!authentication.HasValidAccessToken && !authentication.AuthenticateWithLogin(Username, Password))
|
||||
{
|
||||
//todo: this fails even on network-related issues. we should probably handle those differently.
|
||||
//NotificationManager.ShowMessage("Login failed!");
|
||||
@@ -111,6 +129,17 @@ namespace osu.Game.Online.API
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var userReq = new GetUserRequest();
|
||||
userReq.Success += (u) => {
|
||||
LocalUser.Value = u;
|
||||
};
|
||||
if (!handleRequest(userReq))
|
||||
{
|
||||
State = APIState.Failing;
|
||||
continue;
|
||||
}
|
||||
|
||||
//we're connected!
|
||||
State = APIState.Online;
|
||||
failureCount = 0;
|
||||
@@ -142,7 +171,17 @@ namespace osu.Game.Online.API
|
||||
private void ClearCredentials()
|
||||
{
|
||||
Username = null;
|
||||
password = null;
|
||||
Password = null;
|
||||
}
|
||||
|
||||
public void Login(string username, string password)
|
||||
{
|
||||
Debug.Assert(State == APIState.Offline);
|
||||
|
||||
Username = username;
|
||||
Password = password;
|
||||
|
||||
State = APIState.Connecting;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -154,6 +193,7 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Log($@"Performing request {req}", LoggingTarget.Network);
|
||||
req.Perform(this);
|
||||
|
||||
State = APIState.Online;
|
||||
@@ -221,6 +261,7 @@ namespace osu.Game.Online.API
|
||||
log.Add($@"We just went {newState}!");
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
components.ForEach(c => c.APIStateChanged(this, newState));
|
||||
OnStateChange?.Invoke(oldState, newState);
|
||||
});
|
||||
}
|
||||
@@ -237,29 +278,6 @@ namespace osu.Game.Online.API
|
||||
|
||||
public delegate void StateChangeDelegate(APIState oldState, APIState newState);
|
||||
|
||||
public enum APIState
|
||||
{
|
||||
/// <summary>
|
||||
/// We cannot login (not enough credentials).
|
||||
/// </summary>
|
||||
Offline,
|
||||
|
||||
/// <summary>
|
||||
/// We are having connectivity issues.
|
||||
/// </summary>
|
||||
Failing,
|
||||
|
||||
/// <summary>
|
||||
/// We are in the process of (re-)connecting.
|
||||
/// </summary>
|
||||
Connecting,
|
||||
|
||||
/// <summary>
|
||||
/// We are online.
|
||||
/// </summary>
|
||||
Online
|
||||
}
|
||||
|
||||
private void flushQueue(bool failOldRequests = true)
|
||||
{
|
||||
var oldQueue = queue;
|
||||
@@ -277,6 +295,7 @@ namespace osu.Game.Online.API
|
||||
|
||||
public void Logout()
|
||||
{
|
||||
ClearCredentials();
|
||||
authentication.Clear();
|
||||
State = APIState.Offline;
|
||||
}
|
||||
@@ -286,4 +305,27 @@ namespace osu.Game.Online.API
|
||||
Scheduler.Update();
|
||||
}
|
||||
}
|
||||
|
||||
public enum APIState
|
||||
{
|
||||
/// <summary>
|
||||
/// We cannot login (not enough credentials).
|
||||
/// </summary>
|
||||
Offline,
|
||||
|
||||
/// <summary>
|
||||
/// We are having connectivity issues.
|
||||
/// </summary>
|
||||
Failing,
|
||||
|
||||
/// <summary>
|
||||
/// We are in the process of (re-)connecting.
|
||||
/// </summary>
|
||||
Connecting,
|
||||
|
||||
/// <summary>
|
||||
/// We are online.
|
||||
/// </summary>
|
||||
Online
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//Copyright (c) 2007-2016 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 System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Game.Online.API
|
||||
{
|
||||
public interface IOnlineComponent
|
||||
{
|
||||
void APIStateChanged(APIAccess api, APIState state);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
var req = new AccessTokenRequestPassword(username, password)
|
||||
{
|
||||
Url = $@"{endpoint}/oauth/access_token",
|
||||
Url = $@"{endpoint}/oauth/token",
|
||||
Method = HttpMethod.POST,
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret
|
||||
@@ -55,7 +55,7 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
var req = new AccessTokenRequestRefresh(refresh)
|
||||
{
|
||||
Url = $@"{endpoint}/oauth/access_token",
|
||||
Url = $@"{endpoint}/oauth/token",
|
||||
Method = HttpMethod.POST,
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
public class GetUserRequest : APIRequest<User>
|
||||
{
|
||||
private int? userId;
|
||||
|
||||
public GetUserRequest(int? userId = null)
|
||||
{
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
protected override string Target => userId.HasValue ? $@"users/{userId}" : @"me";
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -4,15 +4,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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.Transformations;
|
||||
using OpenTK;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Online.Chat.Display
|
||||
namespace osu.Game.Online.Chat.Drawables
|
||||
{
|
||||
public class ChannelDisplay : Container
|
||||
{
|
||||
+1
-1
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Chat.Display
|
||||
namespace osu.Game.Online.Chat.Drawables
|
||||
{
|
||||
public class ChatLine : Container
|
||||
{
|
||||
@@ -10,6 +10,9 @@ namespace osu.Game.Online
|
||||
[JsonProperty(@"username")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty(@"id")]
|
||||
public int Id;
|
||||
|
||||
[JsonProperty(@"colour")]
|
||||
public string Colour;
|
||||
}
|
||||
|
||||
+28
-16
@@ -19,7 +19,9 @@ using osu.Framework.Logging;
|
||||
using osu.Game.Graphics.UserInterface.Volume;
|
||||
using osu.Game.Database;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Overlays.Toolbar;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play;
|
||||
@@ -30,7 +32,7 @@ namespace osu.Game
|
||||
{
|
||||
public Toolbar Toolbar;
|
||||
|
||||
private ChatConsole chat;
|
||||
private ChatOverlay chat;
|
||||
|
||||
private MusicController musicController;
|
||||
|
||||
@@ -45,7 +47,7 @@ namespace osu.Game
|
||||
|
||||
string[] args;
|
||||
|
||||
public OptionsOverlay Options;
|
||||
private OptionsOverlay options;
|
||||
|
||||
public OsuGame(string[] args = null)
|
||||
{
|
||||
@@ -59,7 +61,7 @@ namespace osu.Game
|
||||
host.Size = new Vector2(Config.Get<int>(OsuConfig.Width), Config.Get<int>(OsuConfig.Height));
|
||||
}
|
||||
|
||||
public void ToggleOptions() => Options.ToggleVisibility();
|
||||
public void ToggleOptions() => options.ToggleVisibility();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@@ -97,12 +99,7 @@ namespace osu.Game
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
volume = new VolumeControl
|
||||
{
|
||||
VolumeGlobal = Audio.Volume,
|
||||
VolumeSample = Audio.VolumeSample,
|
||||
VolumeTrack = Audio.VolumeTrack
|
||||
},
|
||||
volume = new VolumeControl(),
|
||||
overlayContent = new Container{ RelativeSizeAxes = Axes.Both },
|
||||
new GlobalHotkeys //exists because UserInputManager is at a level below us.
|
||||
{
|
||||
@@ -120,16 +117,18 @@ namespace osu.Game
|
||||
});
|
||||
|
||||
//overlay elements
|
||||
(chat = new ChatConsole(API) { Depth = 0 }).Preload(this, overlayContent.Add);
|
||||
(Options = new OptionsOverlay { Depth = 1 }).Preload(this, overlayContent.Add);
|
||||
(musicController = new MusicController() { Depth = 3 }).Preload(this, overlayContent.Add);
|
||||
(chat = new ChatOverlay { Depth = 0 }).Preload(this, overlayContent.Add);
|
||||
(options = new OptionsOverlay { Depth = -1 }).Preload(this, overlayContent.Add);
|
||||
(musicController = new MusicController() { Depth = -3 }).Preload(this, overlayContent.Add);
|
||||
|
||||
Dependencies.Cache(options);
|
||||
Dependencies.Cache(musicController);
|
||||
|
||||
(Toolbar = new Toolbar
|
||||
{
|
||||
Depth = 2,
|
||||
Depth = -2,
|
||||
OnHome = delegate { mainMenu?.MakeCurrent(); },
|
||||
OnSettings = Options.ToggleVisibility,
|
||||
OnPlayModeChange = delegate (PlayMode m) { PlayMode.Value = m; },
|
||||
OnMusicController = musicController.ToggleVisibility
|
||||
}).Preload(this, t =>
|
||||
{
|
||||
PlayMode.ValueChanged += delegate { Toolbar.SetGameMode(PlayMode.Value); };
|
||||
@@ -137,6 +136,19 @@ namespace osu.Game
|
||||
overlayContent.Add(Toolbar);
|
||||
});
|
||||
|
||||
options.StateChanged += delegate
|
||||
{
|
||||
switch (options.State)
|
||||
{
|
||||
case Visibility.Hidden:
|
||||
intro.MoveToX(0, OptionsOverlay.TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
break;
|
||||
case Visibility.Visible:
|
||||
intro.MoveToX(OptionsOverlay.SIDEBAR_WIDTH / 2, OptionsOverlay.TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Cursor.Alpha = 0;
|
||||
}
|
||||
|
||||
@@ -154,7 +166,7 @@ namespace osu.Game
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.O:
|
||||
Options.ToggleVisibility();
|
||||
options.ToggleVisibility();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+20
-5
@@ -16,12 +16,14 @@ using osu.Game.Database;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Processing;
|
||||
using osu.Game.IPC;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Online.API.Requests;
|
||||
|
||||
namespace osu.Game
|
||||
{
|
||||
public class OsuGameBase : BaseGame
|
||||
public class OsuGameBase : BaseGame, IOnlineComponent
|
||||
{
|
||||
internal OsuConfigManager Config;
|
||||
|
||||
@@ -41,11 +43,11 @@ namespace osu.Game
|
||||
private void load()
|
||||
{
|
||||
Dependencies.Cache(this);
|
||||
Dependencies.Cache(new OsuConfigManager(Host.Storage));
|
||||
Dependencies.Cache(Config);
|
||||
Dependencies.Cache(new BeatmapDatabase(Host.Storage, Host));
|
||||
|
||||
|
||||
//this completely overrides the framework default. will need to change once we make a proper FontStore.
|
||||
Dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 0.01f });
|
||||
Dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 0.01f }, true);
|
||||
|
||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome"));
|
||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont"));
|
||||
@@ -73,6 +75,19 @@ namespace osu.Game
|
||||
Password = Config.Get<string>(OsuConfig.Password),
|
||||
Token = Config.Get<string>(OsuConfig.Token)
|
||||
});
|
||||
|
||||
API.Register(this);
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Online:
|
||||
Config.Set(OsuConfig.Username, Config.Get<bool>(OsuConfig.SaveUsername) ? API.Username : string.Empty);
|
||||
Config.Set(OsuConfig.Password, Config.Get<bool>(OsuConfig.SavePassword) ? API.Password : string.Empty);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -83,7 +98,7 @@ namespace osu.Game
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
Cursor = new OsuCursorContainer { Depth = float.MaxValue }
|
||||
Cursor = new OsuCursorContainer { Depth = float.MinValue }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//Copyright (c) 2007-2016 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -16,11 +16,11 @@ using osu.Framework.Threading;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Chat;
|
||||
using osu.Game.Online.Chat.Display;
|
||||
using osu.Game.Online.Chat.Drawables;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public class ChatConsole : OverlayContainer
|
||||
public class ChatOverlay : OverlayContainer, IOnlineComponent
|
||||
{
|
||||
private ChannelDisplay channelDisplay;
|
||||
|
||||
@@ -32,10 +32,8 @@ namespace osu.Game.Overlays
|
||||
|
||||
private APIAccess api;
|
||||
|
||||
public ChatConsole(APIAccess api)
|
||||
public ChatOverlay()
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Size = new Vector2(1, 300);
|
||||
Anchor = Anchor.BottomLeft;
|
||||
@@ -45,7 +43,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Depth = float.MinValue,
|
||||
Depth = float.MaxValue,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(0.1f, 0.1f, 0.1f, 0.4f),
|
||||
},
|
||||
@@ -57,50 +55,16 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(APIAccess api)
|
||||
{
|
||||
initializeChannels();
|
||||
this.api = api;
|
||||
api.Register(this);
|
||||
}
|
||||
|
||||
private long? lastMessageId;
|
||||
|
||||
private List<Channel> careChannels;
|
||||
|
||||
private void initializeChannels()
|
||||
{
|
||||
careChannels = new List<Channel>();
|
||||
|
||||
//if (api.State != APIAccess.APIState.Online)
|
||||
// return;
|
||||
|
||||
SpriteText loading;
|
||||
Add(loading = new SpriteText
|
||||
{
|
||||
Text = @"Loading available channels...",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = 40,
|
||||
});
|
||||
|
||||
messageRequest?.Cancel();
|
||||
|
||||
ListChannelsRequest req = new ListChannelsRequest();
|
||||
req.Success += delegate (List<Channel> channels)
|
||||
{
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
loading.FadeOut(100);
|
||||
addChannel(channels.Find(c => c.Name == @"#osu"));
|
||||
});
|
||||
|
||||
//addChannel(channels.Find(c => c.Name == @"#lobby"));
|
||||
//addChannel(channels.Find(c => c.Name == @"#english"));
|
||||
|
||||
messageRequest = Scheduler.AddDelayed(() => FetchNewMessages(api), 1000, true);
|
||||
};
|
||||
api.Queue(req);
|
||||
}
|
||||
|
||||
private void addChannel(Channel channel)
|
||||
{
|
||||
Add(channelDisplay = new ChannelDisplay(channel));
|
||||
@@ -145,8 +109,58 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
MoveToY(DrawSize.Y, transition_length, EasingTypes.InQuint);
|
||||
FadeOut(transition_length, EasingTypes.InQuint);
|
||||
MoveToY(DrawSize.Y, transition_length, EasingTypes.InSine);
|
||||
FadeOut(transition_length, EasingTypes.InSine);
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case APIState.Online:
|
||||
initializeChannels();
|
||||
break;
|
||||
default:
|
||||
messageRequest?.Cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeChannels()
|
||||
{
|
||||
Clear();
|
||||
|
||||
careChannels = new List<Channel>();
|
||||
|
||||
//if (api.State != APIAccess.APIState.Online)
|
||||
// return;
|
||||
|
||||
SpriteText loading;
|
||||
Add(loading = new SpriteText
|
||||
{
|
||||
Text = @"Loading available channels...",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = 40,
|
||||
});
|
||||
|
||||
messageRequest?.Cancel();
|
||||
|
||||
ListChannelsRequest req = new ListChannelsRequest();
|
||||
req.Success += delegate (List<Channel> channels)
|
||||
{
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
loading.FadeOut(100);
|
||||
addChannel(channels.Find(c => c.Name == @"#osu"));
|
||||
});
|
||||
|
||||
//addChannel(channels.Find(c => c.Name == @"#lobby"));
|
||||
//addChannel(channels.Find(c => c.Name == @"#english"));
|
||||
|
||||
messageRequest = Scheduler.AddDelayed(() => FetchNewMessages(api), 1000, true);
|
||||
};
|
||||
api.Queue(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,18 @@ namespace osu.Game.Overlays
|
||||
public Action<float> SeekRequested;
|
||||
private bool isDragging;
|
||||
|
||||
private bool enabled;
|
||||
public bool IsEnabled
|
||||
{
|
||||
get { return enabled; }
|
||||
set
|
||||
{
|
||||
enabled = value;
|
||||
if (!enabled)
|
||||
fill.Width = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public DragBar()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@@ -34,13 +46,14 @@ namespace osu.Game.Overlays
|
||||
|
||||
public void UpdatePosition(float position)
|
||||
{
|
||||
if (isDragging) return;
|
||||
if (isDragging || !IsEnabled) return;
|
||||
|
||||
fill.Width = position;
|
||||
}
|
||||
|
||||
private void seek(InputState state)
|
||||
{
|
||||
if (!IsEnabled) return;
|
||||
float seekLocation = state.Mouse.Position.X / DrawWidth;
|
||||
SeekRequested?.Invoke(seekLocation);
|
||||
fill.Width = seekLocation;
|
||||
|
||||
@@ -240,6 +240,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
private void workingChanged(object sender = null, EventArgs e = null)
|
||||
{
|
||||
progress.IsEnabled = (beatmapSource.Value != null);
|
||||
if (beatmapSource.Value == current) return;
|
||||
bool audioEquals = current?.BeatmapInfo.AudioEquals(beatmapSource.Value.BeatmapInfo) ?? false;
|
||||
current = beatmapSource.Value;
|
||||
@@ -386,7 +387,7 @@ namespace osu.Game.Overlays
|
||||
this.beatmap = beatmap;
|
||||
CacheDrawnFrameBuffer = true;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Depth = float.MinValue;
|
||||
Depth = float.MaxValue;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using OpenTK;
|
||||
using System;
|
||||
using OpenTK;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -10,38 +11,72 @@ using osu.Game.Online.API;
|
||||
|
||||
namespace osu.Game.Overlays.Options.General
|
||||
{
|
||||
public class LoginOptions : OptionsSubsection
|
||||
public class LoginOptions : OptionsSubsection, IOnlineComponent
|
||||
{
|
||||
private Container loginForm;
|
||||
protected override string Header => "Sign In";
|
||||
|
||||
public LoginOptions()
|
||||
{
|
||||
Children = new[]
|
||||
{
|
||||
loginForm = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new[] { new LoadingAnimation() }
|
||||
}
|
||||
};
|
||||
}
|
||||
private Action performLogout;
|
||||
protected override string Header => "Sign In";
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(APIAccess api)
|
||||
{
|
||||
api?.Register(this);
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
if (api == null)
|
||||
return;
|
||||
loginForm.Children = new Drawable[]
|
||||
switch (state)
|
||||
{
|
||||
new LoginForm(api)
|
||||
};
|
||||
case APIState.Offline:
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new LoginForm()
|
||||
};
|
||||
break;
|
||||
case APIState.Failing:
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteText
|
||||
{
|
||||
Text = "Connection failing :(",
|
||||
},
|
||||
};
|
||||
break;
|
||||
case APIState.Connecting:
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteText
|
||||
{
|
||||
Text = "Connecting...",
|
||||
},
|
||||
};
|
||||
break;
|
||||
case APIState.Online:
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteText
|
||||
{
|
||||
Text = $"Connected as {api.Username}!",
|
||||
},
|
||||
new OsuButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Sign out",
|
||||
Action = api.Logout
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class LoginForm : FlowContainer
|
||||
{
|
||||
public LoginForm(APIAccess api)
|
||||
private TextBox username;
|
||||
private TextBox password;
|
||||
private APIAccess api;
|
||||
|
||||
public LoginForm()
|
||||
{
|
||||
Direction = FlowDirection.VerticalOnly;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
@@ -51,16 +86,29 @@ namespace osu.Game.Overlays.Options.General
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteText { Text = "Username" },
|
||||
new TextBox { Height = 20, RelativeSizeAxes = Axes.X, Text = api?.Username ?? string.Empty },
|
||||
username = new TextBox { Height = 20, RelativeSizeAxes = Axes.X, Text = api?.Username ?? string.Empty },
|
||||
new SpriteText { Text = "Password" },
|
||||
new TextBox { Height = 20, RelativeSizeAxes = Axes.X },
|
||||
password = new PasswordTextBox { Height = 20, RelativeSizeAxes = Axes.X },
|
||||
new OsuButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Log in",
|
||||
Action = performLogin
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void performLogin()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text))
|
||||
api.Login(username.Text, password.Text);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(APIAccess api)
|
||||
{
|
||||
this.api = api;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,12 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
internal const float CONTENT_MARGINS = 10;
|
||||
|
||||
public const float TRANSITION_LENGTH = 600;
|
||||
|
||||
public const float SIDEBAR_WIDTH = OptionsSidebar.default_width;
|
||||
|
||||
private const float width = 400;
|
||||
private const float sidebar_width = OptionsSidebar.default_width;
|
||||
|
||||
private const float sidebar_padding = 10;
|
||||
|
||||
private ScrollContainer scrollContainer;
|
||||
@@ -71,7 +75,7 @@ namespace osu.Game.Overlays
|
||||
ScrollDraggerVisible = false,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = width,
|
||||
Margin = new MarginPadding { Left = sidebar_width },
|
||||
Margin = new MarginPadding { Left = SIDEBAR_WIDTH },
|
||||
Children = new[]
|
||||
{
|
||||
new FlowContainer
|
||||
@@ -108,7 +112,7 @@ namespace osu.Game.Overlays
|
||||
},
|
||||
sidebar = new OptionsSidebar
|
||||
{
|
||||
Width = sidebar_width,
|
||||
Width = SIDEBAR_WIDTH,
|
||||
Children = sidebarButtons = sections.Select(section =>
|
||||
new SidebarButton
|
||||
{
|
||||
@@ -175,16 +179,16 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
scrollContainer.MoveToX(0, 600, EasingTypes.OutQuint);
|
||||
sidebar.MoveToX(0, 800, EasingTypes.OutQuint);
|
||||
FadeTo(1, 300);
|
||||
scrollContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
sidebar.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
FadeTo(1, TRANSITION_LENGTH / 2);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
scrollContainer.MoveToX(-width, 600, EasingTypes.OutQuint);
|
||||
sidebar.MoveToX(-sidebar_width, 600, EasingTypes.OutQuint);
|
||||
FadeTo(0, 300);
|
||||
scrollContainer.MoveToX(-width, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
sidebar.MoveToX(-SIDEBAR_WIDTH, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
FadeTo(0, TRANSITION_LENGTH / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,37 +2,37 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Online.API;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
public class Toolbar : OverlayContainer
|
||||
{
|
||||
private const float height = 50;
|
||||
|
||||
public Action OnSettings;
|
||||
public Action OnHome;
|
||||
public Action<PlayMode> OnPlayModeChange;
|
||||
public Action OnMusicController;
|
||||
|
||||
private ToolbarModeSelector modeSelector;
|
||||
private ToolbarButton userButton;
|
||||
private Box solidBackground;
|
||||
private Box gradientBackground;
|
||||
|
||||
private const int transition_time = 200;
|
||||
private const int transition_time = 250;
|
||||
|
||||
private const float alpha_hovering = 0.8f;
|
||||
private const float alpha_normal = 0.6f;
|
||||
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
@@ -48,23 +48,26 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
gradientBackground.FadeIn(200);
|
||||
solidBackground.FadeTo(alpha_hovering, transition_time, EasingTypes.OutQuint);
|
||||
gradientBackground.FadeIn(transition_time, EasingTypes.OutQuint);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
gradientBackground.FadeOut(200);
|
||||
solidBackground.FadeTo(alpha_normal, transition_time, EasingTypes.OutQuint);
|
||||
gradientBackground.FadeOut(transition_time, EasingTypes.OutQuint);
|
||||
}
|
||||
|
||||
public Toolbar()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
solidBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(0.1f, 0.1f, 0.1f, 0.6f)
|
||||
Colour = new Color4(0.1f, 0.1f, 0.1f, 1),
|
||||
Alpha = alpha_normal,
|
||||
},
|
||||
gradientBackground = new Box
|
||||
{
|
||||
@@ -81,18 +84,9 @@ namespace osu.Game.Overlays
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new ToolbarButton
|
||||
new ToolbarSettingsButton(),
|
||||
new ToolbarHomeButton()
|
||||
{
|
||||
Icon = FontAwesome.fa_gear,
|
||||
TooltipMain = "Settings",
|
||||
TooltipSub = "Change your settings",
|
||||
Action = () => OnSettings?.Invoke()
|
||||
},
|
||||
new ToolbarButton
|
||||
{
|
||||
Icon = FontAwesome.fa_home,
|
||||
TooltipMain = "Home",
|
||||
TooltipSub = "Return to the main menu",
|
||||
Action = () => OnHome?.Invoke()
|
||||
},
|
||||
modeSelector = new ToolbarModeSelector
|
||||
@@ -110,19 +104,12 @@ namespace osu.Game.Overlays
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new []
|
||||
{
|
||||
new ToolbarButton
|
||||
{
|
||||
Icon = FontAwesome.fa_music,
|
||||
Action = () => OnMusicController?.Invoke()
|
||||
},
|
||||
new ToolbarMusicButton(),
|
||||
new ToolbarButton
|
||||
{
|
||||
Icon = FontAwesome.fa_search
|
||||
},
|
||||
userButton = new ToolbarButton
|
||||
{
|
||||
Icon = FontAwesome.fa_user,
|
||||
},
|
||||
new ToolbarUserButton(),
|
||||
new ToolbarButton
|
||||
{
|
||||
Icon = FontAwesome.fa_bars
|
||||
@@ -135,12 +122,6 @@ namespace osu.Game.Overlays
|
||||
Size = new Vector2(1, height);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
userButton.Text = config.Get<string>(OsuConfig.Username);
|
||||
}
|
||||
|
||||
public void SetGameMode(PlayMode mode) => modeSelector.SetGameMode(mode);
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,19 @@
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
public class ToolbarButton : Container
|
||||
{
|
||||
public const float WIDTH = 60;
|
||||
|
||||
public FontAwesome Icon
|
||||
{
|
||||
get { return DrawableIcon.Icon; }
|
||||
@@ -58,6 +57,7 @@ namespace osu.Game.Overlays
|
||||
private FlowContainer tooltipContainer;
|
||||
private SpriteText tooltip1;
|
||||
private SpriteText tooltip2;
|
||||
protected FlowContainer Flow;
|
||||
|
||||
public ToolbarButton()
|
||||
{
|
||||
@@ -66,16 +66,17 @@ namespace osu.Game.Overlays
|
||||
HoverBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(80, 80, 80, 180),
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Colour = new Color4(60, 60, 60, 255),
|
||||
Alpha = 0,
|
||||
},
|
||||
new FlowContainer
|
||||
Flow = new FlowContainer
|
||||
{
|
||||
Direction = FlowDirection.HorizontalOnly,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Padding = new MarginPadding { Left = 5, Right = 5 },
|
||||
Padding = new MarginPadding { Left = 15, Right = 15 },
|
||||
Spacing = new Vector2(5),
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Children = new Drawable[]
|
||||
@@ -87,7 +88,6 @@ namespace osu.Game.Overlays
|
||||
},
|
||||
DrawableText = new SpriteText
|
||||
{
|
||||
Margin = new MarginPadding { Left = 5 },
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
@@ -118,7 +118,6 @@ namespace osu.Game.Overlays
|
||||
};
|
||||
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Size = new Vector2(WIDTH, 1);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@@ -126,7 +125,7 @@ namespace osu.Game.Overlays
|
||||
base.Update();
|
||||
|
||||
//todo: find a way to avoid using this (autosize needs to be able to ignore certain drawables.. in this case the tooltip)
|
||||
Size = new Vector2(WIDTH + (DrawableText.IsVisible ? DrawableText.DrawSize.X : 0), 1);
|
||||
Size = new Vector2(Flow.DrawSize.X, 1);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
|
||||
@@ -134,21 +133,44 @@ namespace osu.Game.Overlays
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
Action?.Invoke();
|
||||
HoverBackground.FlashColour(Color4.White, 400);
|
||||
HoverBackground.FlashColour(new Color4(255, 255, 255, 100), 500, EasingTypes.OutQuint);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
HoverBackground.FadeTo(0.4f, 200);
|
||||
HoverBackground.FadeIn(200);
|
||||
tooltipContainer.FadeIn(100);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
HoverBackground.FadeTo(0, 200);
|
||||
HoverBackground.FadeOut(200);
|
||||
tooltipContainer.FadeOut(100);
|
||||
}
|
||||
}
|
||||
|
||||
public class OpaqueBackground : Container
|
||||
{
|
||||
public OpaqueBackground()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(30, 30, 30, 255)
|
||||
},
|
||||
new Triangles
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.05f,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
//Copyright (c) 2007-2016 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.Graphics;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
class ToolbarHomeButton : ToolbarButton
|
||||
{
|
||||
public ToolbarHomeButton()
|
||||
{
|
||||
Icon = FontAwesome.fa_home;
|
||||
TooltipMain = "Home";
|
||||
TooltipSub = "Return to the main menu";
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-6
@@ -2,14 +2,12 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
public class ToolbarModeButton : ToolbarButton
|
||||
{
|
||||
@@ -30,7 +28,23 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
set
|
||||
{
|
||||
//Background.Colour = value ? new Color4(100, 100, 100, 255) : new Color4(20, 20, 20, 255);
|
||||
if (value)
|
||||
{
|
||||
DrawableIcon.Colour = Color4.White;
|
||||
DrawableIcon.Masking = true;
|
||||
DrawableIcon.EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = new Color4(255, 194, 224, 100),
|
||||
Radius = 15,
|
||||
Roundness = 15,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawableIcon.Masking = false;
|
||||
DrawableIcon.Colour = new Color4(255, 194, 224, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-19
@@ -3,26 +3,25 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Modes;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
class ToolbarModeSelector : Container
|
||||
{
|
||||
const float padding = 10;
|
||||
|
||||
private FlowContainer modeButtons;
|
||||
private Box modeButtonLine;
|
||||
private Drawable modeButtonLine;
|
||||
private ToolbarModeButton activeButton;
|
||||
|
||||
public Action<PlayMode> OnPlayModeChange;
|
||||
@@ -33,11 +32,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(20, 20, 20, 255)
|
||||
},
|
||||
new OpaqueBackground(),
|
||||
modeButtons = new FlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
@@ -45,14 +40,29 @@ namespace osu.Game.Overlays
|
||||
Direction = FlowDirection.HorizontalOnly,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Padding = new MarginPadding { Left = 10, Right = 10 },
|
||||
},
|
||||
modeButtonLine = new Box
|
||||
modeButtonLine = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Size = new Vector2(0.3f, 3),
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
Colour = Color4.White
|
||||
Origin = Anchor.TopLeft,
|
||||
Masking = true,
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = new Color4(255, 194, 224, 100),
|
||||
Radius = 15,
|
||||
Roundness = 15,
|
||||
},
|
||||
Children = new []
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -72,9 +82,13 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// We need to set the size within LoadComplete, because
|
||||
Size = new Vector2(amountButtons * ToolbarButton.WIDTH + padding * 2, 1);
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Size = new Vector2(modeButtons.DrawSize.X, 1);
|
||||
}
|
||||
|
||||
public void SetGameMode(PlayMode mode)
|
||||
@@ -97,7 +111,7 @@ namespace osu.Game.Overlays
|
||||
base.UpdateLayout();
|
||||
|
||||
if (!activeMode.EnsureValid())
|
||||
activeMode.Refresh(() => modeButtonLine.MoveToX(activeButton.DrawPosition.X + activeButton.DrawSize.X / 2 + padding, 200, EasingTypes.OutQuint));
|
||||
activeMode.Refresh(() => modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, EasingTypes.OutQuint));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//Copyright (c) 2007-2016 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.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
class ToolbarMusicButton : ToolbarOverlayToggleButton
|
||||
{
|
||||
public ToolbarMusicButton()
|
||||
{
|
||||
Icon = FontAwesome.fa_music;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(MusicController music)
|
||||
{
|
||||
StateContainer = music;
|
||||
Action = music.ToggleVisibility;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//Copyright (c) 2007-2016 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework;
|
||||
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.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
class ToolbarOverlayToggleButton : ToolbarButton
|
||||
{
|
||||
private Box StateBackground;
|
||||
|
||||
private OverlayContainer stateContainer;
|
||||
|
||||
public OverlayContainer StateContainer
|
||||
{
|
||||
get { return stateContainer; }
|
||||
set
|
||||
{
|
||||
stateContainer = value;
|
||||
stateContainer.StateChanged += stateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public ToolbarOverlayToggleButton()
|
||||
{
|
||||
Add(StateBackground = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(150, 150, 150, 180),
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Depth = 2,
|
||||
Alpha = 0,
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
if (stateContainer != null)
|
||||
stateContainer.StateChanged -= stateChanged;
|
||||
}
|
||||
|
||||
private void stateChanged(OverlayContainer c, Visibility state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case Visibility.Hidden:
|
||||
StateBackground.FadeOut(200);
|
||||
break;
|
||||
case Visibility.Visible:
|
||||
StateBackground.FadeIn(200);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//Copyright (c) 2007-2016 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.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
class ToolbarSettingsButton : ToolbarOverlayToggleButton
|
||||
{
|
||||
public ToolbarSettingsButton()
|
||||
{
|
||||
Icon = FontAwesome.fa_gear;
|
||||
TooltipMain = "Settings";
|
||||
TooltipSub = "Change your settings";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OptionsOverlay options)
|
||||
{
|
||||
StateContainer = options;
|
||||
Action = options.ToggleVisibility;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
//Copyright (c) 2007-2016 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
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.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Toolbar
|
||||
{
|
||||
class ToolbarUserButton : ToolbarButton, IOnlineComponent
|
||||
{
|
||||
private Avatar avatar;
|
||||
|
||||
public ToolbarUserButton()
|
||||
{
|
||||
DrawableText.Font = @"Exo2.0-MediumItalic";
|
||||
|
||||
Add(new OpaqueBackground { Depth = 1 });
|
||||
|
||||
Flow.Add(avatar = new Avatar());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(APIAccess api, OsuConfigManager config)
|
||||
{
|
||||
api.Register(this);
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
Text = @"Guest";
|
||||
avatar.UserId = 1;
|
||||
break;
|
||||
case APIState.Online:
|
||||
Text = api.Username;
|
||||
avatar.UserId = api.LocalUser.Value.Id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public class Avatar : Container
|
||||
{
|
||||
public Drawable Sprite;
|
||||
|
||||
private int userId;
|
||||
private OsuGame game;
|
||||
private Texture guestTexture;
|
||||
|
||||
public Avatar()
|
||||
{
|
||||
Size = new Vector2(32);
|
||||
Anchor = Anchor.CentreLeft;
|
||||
Origin = Anchor.CentreLeft;
|
||||
|
||||
CornerRadius = Size.X / 8;
|
||||
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 4,
|
||||
Colour = new Color4(0, 0, 0, 0.1f),
|
||||
};
|
||||
|
||||
Masking = true;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGame game, TextureStore textures)
|
||||
{
|
||||
this.game = game;
|
||||
|
||||
guestTexture = textures.Get(@"Online/avatar-guest@2x");
|
||||
}
|
||||
|
||||
public int UserId
|
||||
{
|
||||
get { return userId; }
|
||||
set
|
||||
{
|
||||
if (userId == value)
|
||||
return;
|
||||
|
||||
userId = value;
|
||||
|
||||
Sprite newSprite;
|
||||
if (userId > 1)
|
||||
newSprite = new OnlineSprite($@"https://a.ppy.sh/{userId}");
|
||||
else
|
||||
newSprite = new Sprite { Texture = guestTexture };
|
||||
|
||||
newSprite.FillMode = FillMode.Fit;
|
||||
|
||||
newSprite.Preload(game, s =>
|
||||
{
|
||||
Sprite?.FadeOut();
|
||||
Sprite?.Expire();
|
||||
Sprite = s;
|
||||
|
||||
Add(s);
|
||||
|
||||
s.FadeInFromZero(200);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class OnlineSprite : Sprite
|
||||
{
|
||||
private readonly string url;
|
||||
private readonly int userId;
|
||||
|
||||
public OnlineSprite(string url)
|
||||
{
|
||||
Debug.Assert(url != null);
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
Texture = textures.Get(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ namespace osu.Game.Screens.Backgrounds
|
||||
float newDepth = 0;
|
||||
if (background != null)
|
||||
{
|
||||
newDepth = background.Depth - 1;
|
||||
newDepth = background.Depth + 1;
|
||||
background.Flush();
|
||||
background.FadeOut(250);
|
||||
background.Expire();
|
||||
|
||||
@@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@@ -17,7 +18,7 @@ namespace osu.Game.Screens
|
||||
{
|
||||
public class GameModeWhiteBox : OsuGameMode
|
||||
{
|
||||
private Button popButton;
|
||||
private BackButton popButton;
|
||||
|
||||
const int transition_time = 1000;
|
||||
|
||||
@@ -113,14 +114,10 @@ namespace osu.Game.Screens
|
||||
},
|
||||
}
|
||||
},
|
||||
popButton = new Button
|
||||
popButton = new BackButton
|
||||
{
|
||||
Text = @"Back",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Size = new Vector2(0.1f, 40),
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Colour = new Color4(235, 51, 153, 255),
|
||||
Alpha = 0,
|
||||
Action = delegate {
|
||||
Exit();
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace osu.Game.Screens.Menu
|
||||
public class Button : Container, IStateful<ButtonState>
|
||||
{
|
||||
private Container iconText;
|
||||
private Box box;
|
||||
private Container box;
|
||||
private Color4 colour;
|
||||
private TextAwesome icon;
|
||||
private string internalName;
|
||||
@@ -51,15 +51,31 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
box = new Box
|
||||
box = new Container
|
||||
{
|
||||
Masking = true,
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = new Color4(0, 0, 0, 0.2f),
|
||||
Roundness = 5,
|
||||
Radius = 8,
|
||||
},
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = colour,
|
||||
Scale = new Vector2(0, 1),
|
||||
Size = boxSize,
|
||||
Shear = new Vector2(ButtonSystem.wedge_width / boxSize.Y, 0),
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
}
|
||||
},
|
||||
iconText = new Container
|
||||
{
|
||||
@@ -71,6 +87,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
icon = new TextAwesome
|
||||
{
|
||||
Shadow = true,
|
||||
Anchor = Anchor.Centre,
|
||||
TextSize = 30,
|
||||
Position = new Vector2(0, 0),
|
||||
@@ -78,6 +95,7 @@ namespace osu.Game.Screens.Menu
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
Shadow = true,
|
||||
Direction = FlowDirection.HorizontalOnly,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@@ -15,6 +17,10 @@ namespace osu.Game.Screens.Menu
|
||||
/// </summary>
|
||||
public Drawable CentreTarget;
|
||||
|
||||
protected override IComparer<Drawable> DepthComparer => new ReverseCreationOrderDepthComparer();
|
||||
|
||||
protected override IEnumerable<Drawable> SortedChildren => base.SortedChildren.Reverse();
|
||||
|
||||
public override Anchor Origin => Anchor.Custom;
|
||||
|
||||
public override Vector2 OriginPosition
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Audio.Track;
|
||||
using osu.Framework.GameModes;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
@@ -34,13 +35,21 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
logo = new OsuLogo()
|
||||
new ParallaxContainer
|
||||
{
|
||||
Alpha = 0,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Interactive = false,
|
||||
Colour = Color4.DarkGray,
|
||||
Ripple = false
|
||||
ParallaxAmount = 0.01f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
logo = new OsuLogo
|
||||
{
|
||||
Alpha = 0,
|
||||
Triangles = false,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Interactive = false,
|
||||
Colour = Color4.DarkGray,
|
||||
Ripple = false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@@ -21,16 +26,27 @@ namespace osu.Game.Screens.Menu
|
||||
private Sprite logo;
|
||||
private CircularContainer logoContainer;
|
||||
private Container logoBounceContainer;
|
||||
private Container logoHoverContainer;
|
||||
private MenuVisualisation vis;
|
||||
|
||||
private Container colourAndTriangles;
|
||||
|
||||
public Action Action;
|
||||
|
||||
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * 0.8f;
|
||||
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.78f;
|
||||
|
||||
private Sprite ripple;
|
||||
|
||||
private Container rippleContainer;
|
||||
|
||||
public bool Triangles
|
||||
{
|
||||
set
|
||||
{
|
||||
colourAndTriangles.Alpha = value ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Contains(Vector2 screenSpacePos)
|
||||
{
|
||||
return logoContainer.Contains(screenSpacePos);
|
||||
@@ -61,41 +77,77 @@ namespace osu.Game.Screens.Menu
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
logoContainer = new CircularContainer
|
||||
logoHoverContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new[]
|
||||
{
|
||||
logo = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
},
|
||||
},
|
||||
rippleContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
ripple = new Sprite()
|
||||
new BufferedContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
logoContainer = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.8f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
colourAndTriangles = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = new Color4(233, 103, 161, 255),
|
||||
},
|
||||
new OsuLogoTriangles
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
logo = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(0.5f),
|
||||
},
|
||||
}
|
||||
},
|
||||
rippleContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
ripple = new Sprite()
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Scale = new Vector2(0.5f),
|
||||
Alpha = 0.15f
|
||||
}
|
||||
}
|
||||
},
|
||||
vis = new MenuVisualisation
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = logo.Size,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Alpha = 0.05f
|
||||
Alpha = 0.2f,
|
||||
}
|
||||
}
|
||||
},
|
||||
vis = new MenuVisualisation
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = logo.Size,
|
||||
BlendingMode = BlendingMode.Additive,
|
||||
Alpha = 0.2f,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,15 +157,15 @@ namespace osu.Game.Screens.Menu
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
logo.Texture = textures.Get(@"Menu/logo");
|
||||
ripple.Texture = textures.Get(@"Menu/logo");
|
||||
logo.Texture = textures.Get(@"Menu/logo@2x");
|
||||
ripple.Texture = textures.Get(@"Menu/logo@2x");
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ripple.ScaleTo(1.1f, 500);
|
||||
ripple.ScaleTo(ripple.Scale * 1.1f, 500);
|
||||
ripple.FadeOut(500);
|
||||
ripple.Loop(300);
|
||||
}
|
||||
@@ -122,14 +174,14 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
if (!Interactive) return false;
|
||||
|
||||
logoBounceContainer.ScaleTo(1.1f, 1000, EasingTypes.Out);
|
||||
logoBounceContainer.ScaleTo(0.9f, 1000, EasingTypes.Out);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
|
||||
logoBounceContainer.ScaleTo(1.2f, 500, EasingTypes.OutElastic);
|
||||
logoBounceContainer.ScaleTo(1f, 500, EasingTypes.OutElastic);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -144,13 +196,39 @@ namespace osu.Game.Screens.Menu
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
if (!Interactive) return false;
|
||||
logoBounceContainer.ScaleTo(1.2f, 500, EasingTypes.OutElastic);
|
||||
logoHoverContainer.ScaleTo(1.2f, 500, EasingTypes.OutElastic);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
logoBounceContainer.ScaleTo(1, 500, EasingTypes.OutElastic);
|
||||
logoHoverContainer.ScaleTo(1, 500, EasingTypes.OutElastic);
|
||||
}
|
||||
|
||||
class OsuLogoTriangles : Triangles
|
||||
{
|
||||
public OsuLogoTriangles()
|
||||
{
|
||||
TriangleScale = 4;
|
||||
Alpha = 1;
|
||||
}
|
||||
|
||||
protected override Triangle CreateTriangle()
|
||||
{
|
||||
var triangle = base.CreateTriangle();
|
||||
triangle.Alpha = 1;
|
||||
triangle.Colour = getTriangleShade();
|
||||
return triangle;
|
||||
}
|
||||
|
||||
private Color4 getTriangleShade()
|
||||
{
|
||||
float val = RNG.NextSingle();
|
||||
return Interpolation.ValueAt(val,
|
||||
new Color4(222, 91, 149, 255),
|
||||
new Color4(255, 125, 183, 255),
|
||||
0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +111,7 @@ namespace osu.Game.Screens
|
||||
{
|
||||
AddInternal(new ParallaxContainer
|
||||
{
|
||||
Depth = float.MinValue,
|
||||
Depth = float.MaxValue,
|
||||
Children = new[]
|
||||
{
|
||||
Background = bg
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
@@ -18,6 +19,8 @@ using OpenTK.Input;
|
||||
using MouseState = osu.Framework.Input.MouseState;
|
||||
using OpenTK;
|
||||
using osu.Framework.GameModes;
|
||||
using osu.Game.Modes.UI;
|
||||
using osu.Game.Screens.Ranking;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
@@ -37,6 +40,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private Ruleset ruleset;
|
||||
|
||||
private ScoreProcessor scoreProcessor;
|
||||
private HitRenderer hitRenderer;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuGameBase game)
|
||||
{
|
||||
@@ -83,13 +89,15 @@ namespace osu.Game.Screens.Play
|
||||
ruleset = Ruleset.GetRuleset(usablePlayMode);
|
||||
|
||||
var scoreOverlay = ruleset.CreateScoreOverlay();
|
||||
var hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects);
|
||||
scoreOverlay.BindProcessor(scoreProcessor = ruleset.CreateScoreProcessor());
|
||||
|
||||
hitRenderer.OnHit += delegate (HitObject h) { scoreOverlay.OnHit(h); };
|
||||
hitRenderer.OnMiss += delegate (HitObject h) { scoreOverlay.OnMiss(h); };
|
||||
hitRenderer = ruleset.CreateHitRendererWith(beatmap.HitObjects);
|
||||
|
||||
hitRenderer.OnJudgement += scoreProcessor.AddJudgement;
|
||||
hitRenderer.OnAllJudged += hitRenderer_OnAllJudged;
|
||||
|
||||
if (Autoplay)
|
||||
hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Armed));
|
||||
hitRenderer.Schedule(() => hitRenderer.DrawableObjects.ForEach(h => h.State = ArmedState.Hit));
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@@ -105,6 +113,18 @@ namespace osu.Game.Screens.Play
|
||||
};
|
||||
}
|
||||
|
||||
private void hitRenderer_OnAllJudged()
|
||||
{
|
||||
Delay(1000);
|
||||
Schedule(delegate
|
||||
{
|
||||
Push(new Results
|
||||
{
|
||||
Score = scoreProcessor.GetScore()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnEntering(GameMode last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
@@ -125,26 +145,28 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
}
|
||||
|
||||
bool leftViaKeyboard;
|
||||
bool rightViaKeyboard;
|
||||
|
||||
protected override void TransformState(InputState state)
|
||||
{
|
||||
base.TransformState(state);
|
||||
|
||||
MouseState mouse = (MouseState)state.Mouse;
|
||||
|
||||
foreach (Key k in state.Keyboard.Keys)
|
||||
if (state.Keyboard != null)
|
||||
{
|
||||
switch (k)
|
||||
{
|
||||
case Key.Z:
|
||||
mouse.ButtonStates.Find(s => s.Button == MouseButton.Left).State = true;
|
||||
break;
|
||||
case Key.X:
|
||||
mouse.ButtonStates.Find(s => s.Button == MouseButton.Right).State = true;
|
||||
break;
|
||||
}
|
||||
leftViaKeyboard = state.Keyboard.Keys.Contains(Key.Z);
|
||||
rightViaKeyboard = state.Keyboard.Keys.Contains(Key.X);
|
||||
}
|
||||
|
||||
if (state.Mouse != null)
|
||||
{
|
||||
if (leftViaKeyboard) mouse.ButtonStates.Find(s => s.Button == MouseButton.Left).State = true;
|
||||
if (rightViaKeyboard) mouse.ButtonStates.Find(s => s.Button == MouseButton.Right).State = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,29 @@
|
||||
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.GameModes;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Ranking
|
||||
{
|
||||
class Results : GameModeWhiteBox
|
||||
class Results : OsuGameMode
|
||||
{
|
||||
protected override BackgroundMode CreateBackground() => new BackgroundModeCustom(@"Backgrounds/bg4");
|
||||
protected override BackgroundMode CreateBackground() => new BackgroundModeBeatmap(Beatmap);
|
||||
|
||||
private static readonly Vector2 BACKGROUND_BLUR = new Vector2(20);
|
||||
|
||||
ScoreDisplay scoreDisplay;
|
||||
|
||||
protected override void OnEntering(GameMode last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500));
|
||||
Background.Schedule(() => (Background as BackgroundModeBeatmap)?.BlurTo(BACKGROUND_BLUR, 1000));
|
||||
}
|
||||
|
||||
protected override bool OnExiting(GameMode next)
|
||||
@@ -22,5 +32,63 @@ namespace osu.Game.Screens.Ranking
|
||||
Background.Schedule(() => Background.FadeColour(Color4.White, 500));
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
public Score Score
|
||||
{
|
||||
set
|
||||
{
|
||||
scoreDisplay?.FadeOut(500);
|
||||
scoreDisplay?.Expire();
|
||||
|
||||
scoreDisplay = new ScoreDisplay(value)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
Add(scoreDisplay);
|
||||
|
||||
scoreDisplay.FadeIn(500);
|
||||
scoreDisplay.ScaleTo(0.1f);
|
||||
scoreDisplay.ScaleTo(1, 1000, EasingTypes.OutElastic);
|
||||
scoreDisplay.RotateTo(360 * 5, 1000, EasingTypes.OutElastic);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ScoreDisplay : Container
|
||||
{
|
||||
public ScoreDisplay(Score s)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FlowDirection.VerticalOnly,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteText
|
||||
{
|
||||
TextSize = 40,
|
||||
Text = $@"Accuracy: {s.Accuracy:#0.00%}",
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
TextSize = 40,
|
||||
Text = $@"Score: {s.TotalScore}",
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
TextSize = 40,
|
||||
Text = $@"MaxCombo: {s.MaxCombo}",
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+43
-13
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -8,31 +10,52 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
class BeatmapInfoOverlay : Container
|
||||
class BeatmapInfoWedge : Container
|
||||
{
|
||||
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
|
||||
|
||||
private Container beatmapInfoContainer;
|
||||
|
||||
private BaseGame game;
|
||||
|
||||
public BeatmapInfoWedge()
|
||||
{
|
||||
Shear = wedged_container_shear;
|
||||
Masking = true;
|
||||
BorderColour = new Color4(221, 255, 255, 255);
|
||||
BorderThickness = 2.5f;
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = new Color4(130, 204, 255, 150),
|
||||
Radius = 20,
|
||||
Roundness = 15,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BaseGame game)
|
||||
{
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public void UpdateBeatmap(WorkingBeatmap beatmap)
|
||||
{
|
||||
if (beatmap == null)
|
||||
return;
|
||||
|
||||
float newDepth = 0;
|
||||
if (beatmapInfoContainer != null)
|
||||
{
|
||||
newDepth = beatmapInfoContainer.Depth - 1;
|
||||
beatmapInfoContainer.FadeOut(250);
|
||||
beatmapInfoContainer.Expire();
|
||||
}
|
||||
var lastContainer = beatmapInfoContainer;
|
||||
|
||||
FadeIn(250);
|
||||
float newDepth = lastContainer?.Depth + 1 ?? 0;
|
||||
|
||||
BeatmapSetInfo beatmapSetInfo = beatmap.BeatmapSetInfo;
|
||||
BeatmapInfo beatmapInfo = beatmap.BeatmapInfo;
|
||||
Add(beatmapInfoContainer = new BufferedContainer
|
||||
|
||||
(beatmapInfoContainer = new BufferedContainer
|
||||
{
|
||||
Depth = newDepth,
|
||||
PixelSnapping = true,
|
||||
@@ -51,18 +74,17 @@ namespace osu.Game.Screens.Select
|
||||
},
|
||||
// We use a container, such that we can set the colour gradient to go across the
|
||||
// vertices of the masked container instead of the vertices of the (larger) sprite.
|
||||
beatmap.Background == null ? new Container() : new Container
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ColourInfo = ColourInfo.GradientVertical(Color4.White, new Color4(1f, 1f, 1f, 0.3f)),
|
||||
Children = new []
|
||||
{
|
||||
// Zoomed-in and cropped beatmap background
|
||||
new Sprite
|
||||
new BeatmapBackgroundSprite(beatmap)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = beatmap.Background,
|
||||
FillMode = FillMode.Fill,
|
||||
},
|
||||
},
|
||||
@@ -117,6 +139,14 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
}
|
||||
}
|
||||
}).Preload(game, delegate(Drawable d)
|
||||
{
|
||||
FadeIn(250);
|
||||
|
||||
lastContainer?.FadeOut(250);
|
||||
lastContainer?.Expire();
|
||||
|
||||
Add(d);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -10,39 +10,85 @@ using osu.Game.Database;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
class CarouselContainer : ScrollContainer
|
||||
{
|
||||
private Container<Panel> scrollableContent;
|
||||
private List<BeatmapGroup> groups = new List<BeatmapGroup>();
|
||||
private List<Panel> panels = new List<Panel>();
|
||||
|
||||
public BeatmapGroup SelectedGroup { get; private set; }
|
||||
public BeatmapPanel SelectedPanel { get; private set; }
|
||||
|
||||
private List<float> yPositions = new List<float>();
|
||||
|
||||
public CarouselContainer()
|
||||
{
|
||||
DistanceDecayJump = 0.01;
|
||||
|
||||
Add(scrollableContent = new Container<Panel>
|
||||
private List<float> yPositions = new List<float>();
|
||||
private CarouselLifetimeList<Panel> Lifetime;
|
||||
|
||||
public CarouselContainer()
|
||||
{
|
||||
DistanceDecayJump = 0.01;
|
||||
|
||||
Add(scrollableContent = new Container<Panel>(Lifetime = new CarouselLifetimeList<Panel>(DepthComparer))
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
});
|
||||
}
|
||||
|
||||
internal class CarouselLifetimeList<T> : LifetimeList<Panel>
|
||||
{
|
||||
public CarouselLifetimeList(IComparer<Panel> comparer)
|
||||
: base(comparer)
|
||||
{
|
||||
}
|
||||
|
||||
public int StartIndex;
|
||||
public int EndIndex;
|
||||
|
||||
public override bool Update(FrameTimeInfo time)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
});
|
||||
}
|
||||
bool anyAliveChanged = false;
|
||||
|
||||
//check existing items to make sure they haven't died.
|
||||
foreach (var item in AliveItems.ToArray())
|
||||
{
|
||||
item.UpdateTime(time);
|
||||
if (!item.IsAlive)
|
||||
{
|
||||
//todo: make this more efficient
|
||||
int i = IndexOf(item);
|
||||
anyAliveChanged |= CheckItem(item, ref i);
|
||||
}
|
||||
}
|
||||
|
||||
//handle custom range
|
||||
for (int i = StartIndex; i < EndIndex; i++)
|
||||
{
|
||||
var item = this[i];
|
||||
item.UpdateTime(time);
|
||||
anyAliveChanged |= CheckItem(item, ref i);
|
||||
}
|
||||
|
||||
return anyAliveChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddGroup(BeatmapGroup group)
|
||||
{
|
||||
group.State = BeatmapGroupState.Collapsed;
|
||||
|
||||
groups.Add(group);
|
||||
panels.Add(group.Header);
|
||||
|
||||
group.Header.Depth = -scrollableContent.Children.Count();
|
||||
scrollableContent.Add(group.Header);
|
||||
|
||||
foreach (BeatmapPanel panel in group.BeatmapPanels)
|
||||
panels.Add(panel);
|
||||
{
|
||||
panel.Depth = -scrollableContent.Children.Count();
|
||||
scrollableContent.Add(panel);
|
||||
}
|
||||
|
||||
computeYPositions();
|
||||
}
|
||||
@@ -74,12 +120,19 @@ namespace osu.Game.Screens.Select
|
||||
if (group.State == BeatmapGroupState.Expanded)
|
||||
{
|
||||
group.Header.MoveToX(-100, 500, EasingTypes.OutExpo);
|
||||
var headerY = group.Header.Position.Y;
|
||||
|
||||
foreach (BeatmapPanel panel in group.BeatmapPanels)
|
||||
{
|
||||
if (panel == SelectedPanel)
|
||||
selectedY = currentY + panel.DrawHeight / 2 - DrawHeight / 2;
|
||||
|
||||
panel.MoveToX(-50, 500, EasingTypes.OutExpo);
|
||||
|
||||
//on first display we want to begin hidden under our group's header.
|
||||
if (panel.Alpha == 0)
|
||||
panel.MoveToY(headerY);
|
||||
|
||||
movePanel(panel, true, ref currentY);
|
||||
}
|
||||
}
|
||||
@@ -88,7 +141,10 @@ namespace osu.Game.Screens.Select
|
||||
group.Header.MoveToX(0, 500, EasingTypes.OutExpo);
|
||||
|
||||
foreach (BeatmapPanel panel in group.BeatmapPanels)
|
||||
{
|
||||
panel.MoveToX(0, 500, EasingTypes.OutExpo);
|
||||
movePanel(panel, false, ref currentY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,11 +170,7 @@ namespace osu.Game.Screens.Select
|
||||
public void SelectGroup(BeatmapGroup group, BeatmapPanel panel)
|
||||
{
|
||||
if (SelectedGroup != null && SelectedGroup != group)
|
||||
{
|
||||
SelectedGroup.State = BeatmapGroupState.Collapsed;
|
||||
foreach (BeatmapPanel p in group.BeatmapPanels)
|
||||
p.MoveToY(group.Header.Position.Y);
|
||||
}
|
||||
|
||||
SelectedGroup = group;
|
||||
panel.State = PanelSelectedState.Selected;
|
||||
@@ -128,7 +180,7 @@ namespace osu.Game.Screens.Select
|
||||
ScrollTo(selectedY);
|
||||
}
|
||||
|
||||
private static float offsetX(Panel panel, float dist, float halfHeight)
|
||||
private static float offsetX(float dist, float halfHeight)
|
||||
{
|
||||
// The radius of the circle the carousel moves on.
|
||||
const float CIRCLE_RADIUS = 4;
|
||||
@@ -138,28 +190,16 @@ namespace osu.Game.Screens.Select
|
||||
return 125 + x;
|
||||
}
|
||||
|
||||
private void addPanel(int index)
|
||||
{
|
||||
Panel panel = panels[index];
|
||||
if (panel.Hidden)
|
||||
return;
|
||||
|
||||
if (!scrollableContent.Contains(panel))
|
||||
{
|
||||
panel.Depth = index + (panel is BeatmapSetHeader ? panels.Count : 0);
|
||||
scrollableContent.Add(panel);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
float drawHeight = DrawHeight;
|
||||
scrollableContent.RemoveAll(delegate (Panel p)
|
||||
|
||||
Lifetime.AliveItems.ForEach(delegate (Panel p)
|
||||
{
|
||||
float panelPosY = p.Position.Y;
|
||||
return panelPosY < Current - p.DrawHeight || panelPosY > Current + drawHeight || !IsVisible;
|
||||
p.IsOnScreen = panelPosY >= Current - p.DrawHeight && panelPosY <= Current + drawHeight;
|
||||
});
|
||||
|
||||
int firstIndex = yPositions.BinarySearch(Current - Panel.MAX_HEIGHT);
|
||||
@@ -167,19 +207,24 @@ namespace osu.Game.Screens.Select
|
||||
int lastIndex = yPositions.BinarySearch(Current + drawHeight);
|
||||
if (lastIndex < 0) lastIndex = ~lastIndex;
|
||||
|
||||
for (int i = firstIndex; i < lastIndex; ++i)
|
||||
addPanel(i);
|
||||
Lifetime.StartIndex = firstIndex;
|
||||
Lifetime.EndIndex = lastIndex;
|
||||
|
||||
float halfHeight = drawHeight / 2;
|
||||
foreach (Panel panel in scrollableContent.Children)
|
||||
|
||||
for (int i = firstIndex; i < lastIndex; ++i)
|
||||
{
|
||||
var panel = Lifetime[i];
|
||||
|
||||
panel.IsOnScreen = true;
|
||||
|
||||
float panelDrawY = panel.Position.Y - Current + panel.DrawHeight / 2;
|
||||
float dist = Math.Abs(1f - panelDrawY / halfHeight);
|
||||
|
||||
// Setting the origin position serves as an additive position on top of potential
|
||||
// local transformation we may want to apply (e.g. when a panel gets selected, we
|
||||
// may want to smoothly transform it leftwards.)
|
||||
panel.OriginPosition = new Vector2(-offsetX(panel, dist, halfHeight), 0);
|
||||
panel.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
|
||||
|
||||
// We are applying a multiplicative alpha (which is internally done by nesting an
|
||||
// additional container and setting that container's alpha) such that we can
|
||||
|
||||
@@ -19,6 +19,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Modes;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Screens.Play;
|
||||
@@ -38,9 +39,8 @@ namespace osu.Game.Screens.Select
|
||||
private TrackManager trackManager;
|
||||
|
||||
private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 225);
|
||||
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
|
||||
private static readonly Vector2 wedged_container_start_position = new Vector2(0, 50);
|
||||
private BeatmapInfoOverlay wedgedBeatmapInfoOverlay;
|
||||
private BeatmapInfoWedge beatmapInfoWedge;
|
||||
|
||||
private static readonly Vector2 BACKGROUND_BLUR = new Vector2(20);
|
||||
private CancellationTokenSource initialAddSetsTask;
|
||||
@@ -73,11 +73,8 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="database">Optionally provide a database to use instead of the OsuGame one.</param>
|
||||
public PlaySongSelect(BeatmapDatabase database = null)
|
||||
public PlaySongSelect()
|
||||
{
|
||||
this.database = database;
|
||||
|
||||
const float carouselWidth = 640;
|
||||
const float bottomToolHeight = 50;
|
||||
Children = new Drawable[]
|
||||
@@ -102,24 +99,13 @@ namespace osu.Game.Screens.Select
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
},
|
||||
wedgedBeatmapInfoOverlay = new BeatmapInfoOverlay
|
||||
beatmapInfoWedge = new BeatmapInfoWedge
|
||||
{
|
||||
Alpha = 0,
|
||||
Position = wedged_container_start_position,
|
||||
Size = wedged_container_size,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Shear = wedged_container_shear,
|
||||
Margin = new MarginPadding { Top = 20, Right = 20, },
|
||||
Masking = true,
|
||||
BorderColour = new Color4(221, 255, 255, 255),
|
||||
BorderThickness = 2.5f,
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = new Color4(130, 204, 255, 150),
|
||||
Radius = 20,
|
||||
Roundness = 15,
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
@@ -135,6 +121,13 @@ namespace osu.Game.Screens.Select
|
||||
Size = Vector2.One,
|
||||
Colour = new Color4(0, 0, 0, 0.5f),
|
||||
},
|
||||
new BackButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
//RelativeSizeAxes = Axes.Y,
|
||||
Action = () => Exit()
|
||||
},
|
||||
new Button
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
@@ -239,7 +232,7 @@ namespace osu.Game.Screens.Select
|
||||
(Background as BackgroundModeBeatmap)?.BlurTo(BACKGROUND_BLUR, 1000);
|
||||
}
|
||||
|
||||
wedgedBeatmapInfoOverlay.UpdateBeatmap(beatmap);
|
||||
beatmapInfoWedge.UpdateBeatmap(beatmap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -63,8 +63,15 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Modes\HitJudgementResolver.cs" />
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
|
||||
<Compile Include="Graphics\Backgrounds\Triangles.cs" />
|
||||
<Compile Include="Graphics\Cursor\CursorTrail.cs" />
|
||||
<Compile Include="Graphics\UserInterface\BackButton.cs" />
|
||||
<Compile Include="Modes\Objects\HitObjectParser.cs" />
|
||||
<Compile Include="Modes\Score.cs" />
|
||||
<Compile Include="Modes\ScoreProcesssor.cs" />
|
||||
<Compile Include="Online\API\IOnlineComponent.cs" />
|
||||
<Compile Include="Online\API\Requests\GetUserRequest.cs" />
|
||||
<Compile Include="Overlays\DragBar.cs" />
|
||||
<Compile Include="Overlays\MusicController.cs" />
|
||||
<Compile Include="Beatmaps\Beatmap.cs" />
|
||||
@@ -85,6 +92,11 @@
|
||||
<Compile Include="Beatmaps\Timing\SampleChange.cs" />
|
||||
<Compile Include="Beatmaps\Timing\TimingChange.cs" />
|
||||
<Compile Include="Configuration\OsuConfigManager.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarHomeButton.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarMusicButton.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarSettingsButton.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarOverlayToggleButton.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarUserButton.cs" />
|
||||
<Compile Include="Screens\BackgroundMode.cs" />
|
||||
<Compile Include="Screens\Backgrounds\BackgroundModeBeatmap.cs" />
|
||||
<Compile Include="Screens\Backgrounds\BackgroundModeCustom.cs" />
|
||||
@@ -146,20 +158,20 @@
|
||||
<Compile Include="Online\API\SecurePassword.cs" />
|
||||
<Compile Include="Online\API\Requests\ListChannels.cs" />
|
||||
<Compile Include="Online\Chat\Channel.cs" />
|
||||
<Compile Include="Online\Chat\Display\ChannelDisplay.cs" />
|
||||
<Compile Include="Online\Chat\Display\ChatLine.cs" />
|
||||
<Compile Include="Online\Chat\Drawables\ChannelDisplay.cs" />
|
||||
<Compile Include="Online\Chat\Drawables\ChatLine.cs" />
|
||||
<Compile Include="Online\Chat\Message.cs" />
|
||||
<Compile Include="Online\User.cs" />
|
||||
<Compile Include="OsuGame.cs" />
|
||||
<Compile Include="OsuGameBase.cs" />
|
||||
<Compile Include="Overlays\ChatConsole.cs" />
|
||||
<Compile Include="Overlays\ChatOverlay.cs" />
|
||||
<Compile Include="Overlays\OptionsOverlay.cs" />
|
||||
<Compile Include="Overlays\Toolbar.cs" />
|
||||
<Compile Include="Overlays\ToolbarButton.cs" />
|
||||
<Compile Include="Overlays\ToolbarModeButton.cs" />
|
||||
<Compile Include="Overlays\ToolbarModeSelector.cs" />
|
||||
<Compile Include="Overlays\Toolbar\Toolbar.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarButton.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarModeButton.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarModeSelector.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Screens\Select\BeatmapInfoOverlay.cs" />
|
||||
<Compile Include="Screens\Select\BeatmapInfoWedge.cs" />
|
||||
<Compile Include="Users\User.cs" />
|
||||
<Compile Include="Graphics\UserInterface\Volume\VolumeControl.cs" />
|
||||
<Compile Include="Database\BeatmapDatabase.cs" />
|
||||
|
||||
Reference in New Issue
Block a user