mirror of
https://github.com/ppy/osu
synced 2025-03-18 17:15:28 +00:00
Merge branch 'master' into playsongselect-exit-fix
This commit is contained in:
commit
40312080f7
@ -1 +1 @@
|
||||
Subproject commit 777996fb9731ba1895a5ab1323cbbc97259ff741
|
||||
Subproject commit 0f3db5da09d0e7c4d2ef3057030e018f34ba536e
|
39
osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs
Normal file
39
osu.Desktop.VisualTests/Tests/TestCaseBreadcrumbs.cs
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseBreadcrumbs : TestCase
|
||||
{
|
||||
public override string Description => @"breadcrumb > control";
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
BreadcrumbControl<BreadcrumbTab> c;
|
||||
Add(c = new BreadcrumbControl<BreadcrumbTab>
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.5f,
|
||||
});
|
||||
|
||||
AddStep(@"first", () => c.Current.Value = BreadcrumbTab.Click);
|
||||
AddStep(@"second", () => c.Current.Value = BreadcrumbTab.The);
|
||||
AddStep(@"third", () => c.Current.Value = BreadcrumbTab.Circles);
|
||||
}
|
||||
|
||||
private enum BreadcrumbTab
|
||||
{
|
||||
Click,
|
||||
The,
|
||||
Circles,
|
||||
}
|
||||
}
|
||||
}
|
74
osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs
Normal file
74
osu.Desktop.VisualTests/Tests/TestCaseDrawableRoom.cs
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Screens.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
internal class TestCaseDrawableRoom : TestCase
|
||||
{
|
||||
public override string Description => @"Select your favourite room";
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
DrawableRoom first;
|
||||
DrawableRoom second;
|
||||
Add(new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 500f,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
first = new DrawableRoom(new Room()),
|
||||
second = new DrawableRoom(new Room()),
|
||||
}
|
||||
});
|
||||
|
||||
first.Room.Name.Value = @"Great Room Right Here";
|
||||
first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" }};
|
||||
first.Room.Status.Value = new RoomStatusOpen();
|
||||
first.Room.Beatmap.Value = new BeatmapMetadata { Title = @"Seiryu", Artist = @"Critical Crystal" };
|
||||
|
||||
second.Room.Name.Value = @"Relax It's The Weekend";
|
||||
second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }};
|
||||
second.Room.Status.Value = new RoomStatusPlaying();
|
||||
second.Room.Beatmap.Value = new BeatmapMetadata { Title = @"ZAQ", Artist = @"Serendipity" };
|
||||
|
||||
AddStep(@"change state", () =>
|
||||
{
|
||||
first.Room.Status.Value = new RoomStatusPlaying();
|
||||
});
|
||||
|
||||
AddStep(@"change name", () =>
|
||||
{
|
||||
first.Room.Name.Value = @"I Changed Name";
|
||||
});
|
||||
|
||||
AddStep(@"change host", () =>
|
||||
{
|
||||
first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } };
|
||||
});
|
||||
|
||||
AddStep(@"change beatmap", () =>
|
||||
{
|
||||
first.Room.Beatmap.Value = null;
|
||||
});
|
||||
|
||||
AddStep(@"change state", () =>
|
||||
{
|
||||
first.Room.Status.Value = new RoomStatusOpen();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,9 @@ using OpenTK;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Framework.Configuration;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
@ -59,6 +62,51 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
}
|
||||
};
|
||||
|
||||
Action createPlayfieldWithNotesAcceptingInput = () =>
|
||||
{
|
||||
Clear();
|
||||
|
||||
var rateAdjustClock = new StopwatchClock(true) { Rate = 0.5 };
|
||||
|
||||
ManiaPlayfield playField;
|
||||
Add(playField = new ManiaPlayfield(4, new List<TimingChange> { new TimingChange { BeatLength = 200 } })
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(1, -1),
|
||||
Clock = new FramedClock(rateAdjustClock)
|
||||
});
|
||||
|
||||
for (int t = 1000; t <= 2000; t += 100)
|
||||
{
|
||||
playField.Add(new DrawableNote(new Note
|
||||
{
|
||||
StartTime = t,
|
||||
Column = 0
|
||||
}, new Bindable<Key>(Key.D)));
|
||||
|
||||
playField.Add(new DrawableNote(new Note
|
||||
{
|
||||
StartTime = t,
|
||||
Column = 3
|
||||
}, new Bindable<Key>(Key.K)));
|
||||
}
|
||||
|
||||
playField.Add(new DrawableHoldNote(new HoldNote
|
||||
{
|
||||
StartTime = 1000,
|
||||
Duration = 1000,
|
||||
Column = 1
|
||||
}, new Bindable<Key>(Key.F)));
|
||||
|
||||
playField.Add(new DrawableHoldNote(new HoldNote
|
||||
{
|
||||
StartTime = 1000,
|
||||
Duration = 1000,
|
||||
Column = 2
|
||||
}, new Bindable<Key>(Key.J)));
|
||||
};
|
||||
|
||||
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
|
||||
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
||||
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
|
||||
@ -76,11 +124,13 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
AddWaitStep(10);
|
||||
AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right));
|
||||
AddWaitStep(10);
|
||||
|
||||
AddStep("Notes with input", () => createPlayfieldWithNotesAcceptingInput());
|
||||
}
|
||||
|
||||
private void triggerKeyDown(Column column)
|
||||
{
|
||||
column.TriggerKeyDown(new InputState(), new KeyDownEventArgs
|
||||
column.TriggerOnKeyDown(new InputState(), new KeyDownEventArgs
|
||||
{
|
||||
Key = column.Key,
|
||||
Repeat = false
|
||||
@ -89,7 +139,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
|
||||
private void triggerKeyUp(Column column)
|
||||
{
|
||||
column.TriggerKeyUp(new InputState(), new KeyUpEventArgs
|
||||
column.TriggerOnKeyUp(new InputState(), new KeyUpEventArgs
|
||||
{
|
||||
Key = column.Key
|
||||
});
|
||||
|
@ -32,14 +32,14 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
Username = @"flyte",
|
||||
Id = 3103765,
|
||||
Country = new Country { FlagName = @"JP" },
|
||||
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/3103765/5b012e13611d5761caa7e24fecb3d3a16e1cf48fc2a3032cfd43dd444af83d82.jpeg"
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
|
||||
}) { Width = 300 },
|
||||
peppy = new UserPanel(new User
|
||||
{
|
||||
Username = @"peppy",
|
||||
Id = 2,
|
||||
Country = new Country { FlagName = @"AU" },
|
||||
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/2/08cad88747c235a64fca5f1b770e100f120827ded1ffe3b66bfcd19c940afa65.jpeg"
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg"
|
||||
}) { Width = 300 },
|
||||
},
|
||||
});
|
||||
|
@ -220,8 +220,10 @@
|
||||
<Compile Include="Tests\TestCaseLeaderboard.cs" />
|
||||
<Compile Include="Beatmaps\TestWorkingBeatmap.cs" />
|
||||
<Compile Include="Tests\TestCaseBeatmapDetailArea.cs" />
|
||||
<Compile Include="Tests\TestCaseDrawableRoom.cs" />
|
||||
<Compile Include="Tests\TestCaseUserPanel.cs" />
|
||||
<Compile Include="Tests\TestCaseDirect.cs" />
|
||||
<Compile Include="Tests\TestCaseBreadcrumbs.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup />
|
||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.MathUtils;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
{
|
||||
@ -161,9 +162,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
pattern.Add(new HoldNote
|
||||
{
|
||||
StartTime = HitObject.StartTime,
|
||||
Samples = HitObject.Samples,
|
||||
Duration = endTimeData.Duration,
|
||||
Column = column,
|
||||
Head = { Samples = sampleInfoListAt(HitObject.StartTime) },
|
||||
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
||||
});
|
||||
}
|
||||
else if (positionData != null)
|
||||
@ -178,6 +180,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the sample info list at a point in time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||
/// <returns></returns>
|
||||
private SampleInfoList sampleInfoListAt(double time)
|
||||
{
|
||||
var curveData = HitObject as IHasCurve;
|
||||
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount;
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -471,14 +471,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
}
|
||||
else
|
||||
{
|
||||
newObject = new HoldNote
|
||||
var holdNote = new HoldNote
|
||||
{
|
||||
StartTime = startTime,
|
||||
Samples = sampleInfoListAt(startTime),
|
||||
EndSamples = sampleInfoListAt(endTime),
|
||||
Column = column,
|
||||
Duration = endTime - startTime
|
||||
Duration = endTime - startTime,
|
||||
Head = { Samples = sampleInfoListAt(startTime) },
|
||||
Tail = { Samples = sampleInfoListAt(endTime) }
|
||||
};
|
||||
|
||||
|
||||
newObject = holdNote;
|
||||
}
|
||||
|
||||
pattern.Add(newObject);
|
||||
|
@ -69,18 +69,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
if (holdNote)
|
||||
{
|
||||
newObject = new HoldNote
|
||||
var hold = new HoldNote
|
||||
{
|
||||
StartTime = HitObject.StartTime,
|
||||
EndSamples = HitObject.Samples,
|
||||
Column = column,
|
||||
Duration = endTime - HitObject.StartTime
|
||||
};
|
||||
|
||||
newObject.Samples.Add(new SampleInfo
|
||||
hold.Head.Samples.Add(new SampleInfo
|
||||
{
|
||||
Name = SampleInfo.HIT_NORMAL
|
||||
});
|
||||
|
||||
hold.Tail.Samples = HitObject.Samples;
|
||||
|
||||
newObject = hold;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
37
osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
Normal file
37
osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class HoldNoteTailJudgement : ManiaJudgement
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the hold note has been released too early and shouldn't give full score for the release.
|
||||
/// </summary>
|
||||
public bool HasBroken;
|
||||
|
||||
public override int NumericResultForScore(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return base.NumericResultForScore(result);
|
||||
case ManiaHitResult.Great:
|
||||
case ManiaHitResult.Perfect:
|
||||
return base.NumericResultForScore(HasBroken ? ManiaHitResult.Good : result);
|
||||
}
|
||||
}
|
||||
|
||||
public override int NumericResultForAccuracy(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return base.NumericResultForAccuracy(result);
|
||||
case ManiaHitResult.Great:
|
||||
case ManiaHitResult.Perfect:
|
||||
return base.NumericResultForAccuracy(HasBroken ? ManiaHitResult.Good : result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
Normal file
13
osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class HoldNoteTickJudgement : ManiaJudgement
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
public override int NumericResultForScore(ManiaHitResult result) => 20;
|
||||
public override int NumericResultForAccuracy(ManiaHitResult result) => 0; // Don't count ticks into accuracy
|
||||
}
|
||||
}
|
@ -2,11 +2,37 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class ManiaJudgement : Judgement
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum possible hit result.
|
||||
/// </summary>
|
||||
public const ManiaHitResult MAX_HIT_RESULT = ManiaHitResult.Perfect;
|
||||
|
||||
/// <summary>
|
||||
/// The result value for the combo portion of the score.
|
||||
/// </summary>
|
||||
public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(ManiaResult);
|
||||
|
||||
/// <summary>
|
||||
/// The result value for the accuracy portion of the score.
|
||||
/// </summary>
|
||||
public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(ManiaResult);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum result value for the combo portion of the score.
|
||||
/// </summary>
|
||||
public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum result value for the accuracy portion of the score.
|
||||
/// </summary>
|
||||
public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT);
|
||||
|
||||
public override string ResultString => string.Empty;
|
||||
|
||||
public override string MaxResultString => string.Empty;
|
||||
@ -15,5 +41,42 @@ namespace osu.Game.Rulesets.Mania.Judgements
|
||||
/// The hit result.
|
||||
/// </summary>
|
||||
public ManiaHitResult ManiaResult;
|
||||
|
||||
public virtual int NumericResultForScore(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case ManiaHitResult.Bad:
|
||||
return 50;
|
||||
case ManiaHitResult.Ok:
|
||||
return 100;
|
||||
case ManiaHitResult.Good:
|
||||
return 200;
|
||||
case ManiaHitResult.Great:
|
||||
case ManiaHitResult.Perfect:
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual int NumericResultForAccuracy(ManiaHitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case ManiaHitResult.Bad:
|
||||
return 50;
|
||||
case ManiaHitResult.Ok:
|
||||
return 100;
|
||||
case ManiaHitResult.Good:
|
||||
return 200;
|
||||
case ManiaHitResult.Great:
|
||||
return 300;
|
||||
case ManiaHitResult.Perfect:
|
||||
return 305;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
osu.Game.Rulesets.Mania/Objects/BarLine.cs
Normal file
21
osu.Game.Rulesets.Mania/Objects/BarLine.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
public class BarLine : ManiaHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The control point which this bar line is part of.
|
||||
/// </summary>
|
||||
public TimingControlPoint ControlPoint;
|
||||
|
||||
/// <summary>
|
||||
/// The index of the beat which this bar line represents within the control point.
|
||||
/// This is a "major" bar line if <see cref="BeatIndex"/> % <see cref="TimingControlPoint.TimeSignature"/> == 0.
|
||||
/// </summary>
|
||||
public int BeatIndex;
|
||||
}
|
||||
}
|
74
osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
Normal file
74
osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualises a <see cref="BarLine"/>. Although this derives DrawableManiaHitObject,
|
||||
/// this does not handle input/sound like a normal hit object.
|
||||
/// </summary>
|
||||
public class DrawableBarLine : DrawableManiaHitObject<BarLine>
|
||||
{
|
||||
/// <summary>
|
||||
/// Height of major bar line triangles.
|
||||
/// </summary>
|
||||
private const float triangle_height = 12;
|
||||
|
||||
/// <summary>
|
||||
/// Offset of the major bar line triangles from the sides of the bar line.
|
||||
/// </summary>
|
||||
private const float triangle_offset = 9;
|
||||
|
||||
public DrawableBarLine(BarLine barLine)
|
||||
: base(barLine)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 1;
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
Name = "Bar line",
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
|
||||
bool isMajor = barLine.BeatIndex % (int)barLine.ControlPoint.TimeSignature == 0;
|
||||
|
||||
if (isMajor)
|
||||
{
|
||||
Add(new EquilateralTriangle
|
||||
{
|
||||
Name = "Left triangle",
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(triangle_height),
|
||||
X = -triangle_offset,
|
||||
Rotation = 90
|
||||
});
|
||||
|
||||
Add(new EquilateralTriangle
|
||||
{
|
||||
Name = "Right triangle",
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.TopCentre,
|
||||
Size = new Vector2(triangle_height),
|
||||
X = triangle_offset,
|
||||
Rotation = -90
|
||||
});
|
||||
}
|
||||
|
||||
if (!isMajor && barLine.BeatIndex % 2 == 1)
|
||||
Alpha = 0.2f;
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -7,14 +7,34 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Configuration;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualises a <see cref="HoldNote"/> hit object.
|
||||
/// </summary>
|
||||
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>
|
||||
{
|
||||
private readonly NotePiece headPiece;
|
||||
private readonly DrawableNote head;
|
||||
private readonly DrawableNote tail;
|
||||
|
||||
private readonly BodyPiece bodyPiece;
|
||||
private readonly NotePiece tailPiece;
|
||||
private readonly Container<DrawableHoldNoteTick> tickContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
|
||||
/// </summary>
|
||||
private double? holdStartTime;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the hold note has been released too early and shouldn't give full score for the release.
|
||||
/// </summary>
|
||||
private bool hasBroken;
|
||||
|
||||
public DrawableHoldNote(HoldNote hitObject, Bindable<Key> key = null)
|
||||
: base(hitObject, key)
|
||||
@ -32,17 +52,39 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
},
|
||||
headPiece = new NotePiece
|
||||
tickContainer = new Container<DrawableHoldNoteTick>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativeCoordinateSpace = new Vector2(1, (float)HitObject.Duration)
|
||||
},
|
||||
head = new DrawableHeadNote(this, key)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
tailPiece = new NotePiece
|
||||
tail = new DrawableTailNote(this, key)
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var tick in HitObject.Ticks)
|
||||
{
|
||||
var drawableTick = new DrawableHoldNoteTick(tick)
|
||||
{
|
||||
HoldStartTime = () => holdStartTime
|
||||
};
|
||||
|
||||
// To make the ticks relative to ourselves we need to offset them backwards
|
||||
drawableTick.Y -= (float)HitObject.StartTime;
|
||||
|
||||
tickContainer.Add(drawableTick);
|
||||
AddNested(drawableTick);
|
||||
}
|
||||
|
||||
AddNested(head);
|
||||
AddNested(tail);
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
@ -54,9 +96,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return;
|
||||
base.AccentColour = value;
|
||||
|
||||
headPiece.AccentColour = value;
|
||||
tickContainer.Children.ForEach(t => t.AccentColour = value);
|
||||
|
||||
bodyPiece.AccentColour = value;
|
||||
tailPiece.AccentColour = value;
|
||||
head.AccentColour = value;
|
||||
tail.AccentColour = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,14 +108,132 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (Time.Current > HitObject.StartTime)
|
||||
headPiece.Colour = Color4.Green;
|
||||
if (Time.Current > HitObject.EndTime)
|
||||
// Make sure the keypress happened within the body of the hold note
|
||||
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
|
||||
return false;
|
||||
|
||||
if (args.Key != Key)
|
||||
return false;
|
||||
|
||||
if (args.Repeat)
|
||||
return false;
|
||||
|
||||
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
|
||||
// and within the limited range of the above if-statement. This state will be managed by the head note if the
|
||||
// user has pressed during the hit windows of the head note.
|
||||
holdStartTime = Time.Current;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
||||
{
|
||||
// Make sure that the user started holding the key during the hold note
|
||||
if (!holdStartTime.HasValue)
|
||||
return false;
|
||||
|
||||
if (args.Key != Key)
|
||||
return false;
|
||||
|
||||
holdStartTime = null;
|
||||
|
||||
// If the key has been released too early, the user should not receive full score for the release
|
||||
if (!tail.Judged)
|
||||
hasBroken = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The head note of a hold.
|
||||
/// </summary>
|
||||
private class DrawableHeadNote : DrawableNote
|
||||
{
|
||||
private readonly DrawableHoldNote holdNote;
|
||||
|
||||
public DrawableHeadNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
|
||||
: base(holdNote.HitObject.Head, key)
|
||||
{
|
||||
bodyPiece.Colour = Color4.Green;
|
||||
tailPiece.Colour = Color4.Green;
|
||||
this.holdNote = holdNote;
|
||||
|
||||
RelativePositionAxes = Axes.None;
|
||||
Y = 0;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (!base.OnKeyDown(state, args))
|
||||
return false;
|
||||
|
||||
// We only want to trigger a holding state from the head if the head has received a judgement
|
||||
if (!Judged)
|
||||
return false;
|
||||
|
||||
// If the key has been released too early, the user should not receive full score for the release
|
||||
if (Judgement.Result == HitResult.Miss)
|
||||
holdNote.hasBroken = true;
|
||||
|
||||
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
|
||||
// The body doesn't handle these early early hits, so we have to explicitly set the holding state here
|
||||
holdNote.holdStartTime = Time.Current;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tail note of a hold.
|
||||
/// </summary>
|
||||
private class DrawableTailNote : DrawableNote
|
||||
{
|
||||
private readonly DrawableHoldNote holdNote;
|
||||
|
||||
public DrawableTailNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
|
||||
: base(holdNote.HitObject.Tail, key)
|
||||
{
|
||||
this.holdNote = holdNote;
|
||||
|
||||
RelativePositionAxes = Axes.None;
|
||||
Y = 0;
|
||||
}
|
||||
|
||||
protected override ManiaJudgement CreateJudgement() => new HoldNoteTailJudgement();
|
||||
|
||||
protected override void CheckJudgement(bool userTriggered)
|
||||
{
|
||||
base.CheckJudgement(userTriggered);
|
||||
|
||||
var tailJudgement = Judgement as HoldNoteTailJudgement;
|
||||
if (tailJudgement == null)
|
||||
return;
|
||||
|
||||
tailJudgement.HasBroken = holdNote.hasBroken;
|
||||
}
|
||||
|
||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
||||
{
|
||||
// Make sure that the user started holding the key during the hold note
|
||||
if (!holdNote.holdStartTime.HasValue)
|
||||
return false;
|
||||
|
||||
if (Judgement.Result != HitResult.None)
|
||||
return false;
|
||||
|
||||
if (args.Key != Key)
|
||||
return false;
|
||||
|
||||
UpdateJudgement(true);
|
||||
|
||||
// Handled by the hold note, which will set holding = false
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
// Tail doesn't handle key down
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,121 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualises a <see cref="HoldNoteTick"/> hit object.
|
||||
/// </summary>
|
||||
public class DrawableHoldNoteTick : DrawableManiaHitObject<HoldNoteTick>
|
||||
{
|
||||
/// <summary>
|
||||
/// References the time at which the user started holding the hold note.
|
||||
/// </summary>
|
||||
public Func<double?> HoldStartTime;
|
||||
|
||||
/// <summary>
|
||||
/// References whether the user is currently holding the hold note.
|
||||
/// </summary>
|
||||
public Func<bool> IsHolding;
|
||||
|
||||
private readonly Container glowContainer;
|
||||
|
||||
public DrawableHoldNoteTick(HoldNoteTick hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Size = new Vector2(1);
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
glowContainer = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Set the default glow
|
||||
AccentColour = Color4.White;
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
|
||||
glowContainer.EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Radius = 2f,
|
||||
Roundness = 15f,
|
||||
Colour = value.Opacity(0.3f)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement();
|
||||
|
||||
protected override void CheckJudgement(bool userTriggered)
|
||||
{
|
||||
if (!userTriggered)
|
||||
return;
|
||||
|
||||
if (Time.Current < HitObject.StartTime)
|
||||
return;
|
||||
|
||||
if (HoldStartTime?.Invoke() > HitObject.StartTime)
|
||||
return;
|
||||
|
||||
Judgement.ManiaResult = ManiaHitResult.Perfect;
|
||||
Judgement.Result = HitResult.Hit;
|
||||
}
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
switch (State)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
AccentColour = Color4.Green;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (Judgement.Result != HitResult.None)
|
||||
return;
|
||||
|
||||
if (IsHolding?.Invoke() != true)
|
||||
return;
|
||||
|
||||
UpdateJudgement(true);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,9 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualises a <see cref="Note"/> hit object.
|
||||
/// </summary>
|
||||
public class DrawableNote : DrawableManiaHitObject<Note>
|
||||
{
|
||||
private readonly NotePiece headPiece;
|
||||
|
@ -1,10 +1,9 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Audio;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
@ -12,32 +11,99 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// <summary>
|
||||
/// Represents a hit object which requires pressing, holding, and releasing a key.
|
||||
/// </summary>
|
||||
public class HoldNote : Note, IHasEndTime
|
||||
public class HoldNote : ManiaHitObject, IHasEndTime
|
||||
{
|
||||
/// <summary>
|
||||
/// Lenience of release hit windows. This is to make cases where the hold note release
|
||||
/// is timed alongside presses of other hit objects less awkward.
|
||||
/// </summary>
|
||||
private const double release_window_lenience = 1.5;
|
||||
|
||||
public double Duration { get; set; }
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
/// <summary>
|
||||
/// The samples to be played when this hold note is released.
|
||||
/// </summary>
|
||||
public SampleInfoList EndSamples = new SampleInfoList();
|
||||
private double duration;
|
||||
public double Duration
|
||||
{
|
||||
get { return duration; }
|
||||
set
|
||||
{
|
||||
duration = value;
|
||||
Tail.StartTime = EndTime;
|
||||
}
|
||||
}
|
||||
|
||||
public override double StartTime
|
||||
{
|
||||
get { return base.StartTime; }
|
||||
set
|
||||
{
|
||||
base.StartTime = value;
|
||||
Head.StartTime = value;
|
||||
Tail.StartTime = EndTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key-release hit windows for this hold note.
|
||||
/// The head note of the hold.
|
||||
/// </summary>
|
||||
public HitWindows ReleaseHitWindows { get; protected set; } = new HitWindows();
|
||||
public readonly Note Head = new Note();
|
||||
|
||||
/// <summary>
|
||||
/// The tail note of the hold.
|
||||
/// </summary>
|
||||
public readonly Note Tail = new TailNote();
|
||||
|
||||
/// <summary>
|
||||
/// The time between ticks of this hold.
|
||||
/// </summary>
|
||||
private double tickSpacing = 50;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
|
||||
ReleaseHitWindows = HitWindows * release_window_lenience;
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
|
||||
|
||||
Head.ApplyDefaults(controlPointInfo, difficulty);
|
||||
Tail.ApplyDefaults(controlPointInfo, difficulty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The scoring scoring ticks of the hold note.
|
||||
/// </summary>
|
||||
public IEnumerable<HoldNoteTick> Ticks => ticks ?? (ticks = createTicks());
|
||||
private List<HoldNoteTick> ticks;
|
||||
|
||||
private List<HoldNoteTick> createTicks()
|
||||
{
|
||||
var ret = new List<HoldNoteTick>();
|
||||
|
||||
if (tickSpacing == 0)
|
||||
return ret;
|
||||
|
||||
for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing)
|
||||
{
|
||||
ret.Add(new HoldNoteTick
|
||||
{
|
||||
StartTime = t
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tail of the hold note.
|
||||
/// </summary>
|
||||
private class TailNote : Note
|
||||
{
|
||||
/// <summary>
|
||||
/// Lenience of release hit windows. This is to make cases where the hold note release
|
||||
/// is timed alongside presses of other hit objects less awkward.
|
||||
/// </summary>
|
||||
private const double release_window_lenience = 1.5;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
|
||||
HitWindows *= release_window_lenience;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
Normal file
12
osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
/// <summary>
|
||||
/// A scoring tick of a hold note.
|
||||
/// </summary>
|
||||
public class HoldNoteTick : ManiaHitObject
|
||||
{
|
||||
}
|
||||
}
|
@ -1,8 +1,13 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
@ -10,6 +15,143 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
{
|
||||
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject, ManiaJudgement>
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum score achievable.
|
||||
/// Does _not_ include bonus score - for bonus score see <see cref="bonusScore"/>.
|
||||
/// </summary>
|
||||
private const int max_score = 1000000;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of the score attributed to combo.
|
||||
/// </summary>
|
||||
private const double combo_portion_max = max_score * 0.2;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of the score attributed to accuracy.
|
||||
/// </summary>
|
||||
private const double accuracy_portion_max = max_score * 0.8;
|
||||
|
||||
/// <summary>
|
||||
/// The factor used to determine relevance of combos.
|
||||
/// </summary>
|
||||
private const double combo_base = 4;
|
||||
|
||||
/// <summary>
|
||||
/// The combo value at which hit objects result in the max score possible.
|
||||
/// </summary>
|
||||
private const int combo_relevance_cap = 400;
|
||||
|
||||
/// <summary>
|
||||
/// The hit HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_min = 0.75;
|
||||
|
||||
/// <summary>
|
||||
/// The hit HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_mid = 0.85;
|
||||
|
||||
/// <summary>
|
||||
/// The hit HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_max = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The default BAD hit HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_bad = 0.005;
|
||||
|
||||
/// <summary>
|
||||
/// The default OK hit HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_ok = 0.010;
|
||||
|
||||
/// <summary>
|
||||
/// The default GOOD hit HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_good = 0.035;
|
||||
|
||||
/// <summary>
|
||||
/// The default tick hit HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_tick = 0.040;
|
||||
|
||||
/// <summary>
|
||||
/// The default GREAT hit HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_great = 0.055;
|
||||
|
||||
/// <summary>
|
||||
/// The default PERFECT hit HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_perfect = 0.065;
|
||||
|
||||
/// <summary>
|
||||
/// The MISS HP multiplier at OD = 0.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_miss_min = 0.5;
|
||||
|
||||
/// <summary>
|
||||
/// The MISS HP multiplier at OD = 5.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_miss_mid = 0.75;
|
||||
|
||||
/// <summary>
|
||||
/// The MISS HP multiplier at OD = 10.
|
||||
/// </summary>
|
||||
private const double hp_multiplier_miss_max = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The default MISS HP increase.
|
||||
/// </summary>
|
||||
private const double hp_increase_miss = -0.125;
|
||||
|
||||
/// <summary>
|
||||
/// The MISS HP multiplier. This is multiplied to the miss hp increase.
|
||||
/// </summary>
|
||||
private double hpMissMultiplier = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The HIT HP multiplier. This is multiplied to hit hp increases.
|
||||
/// </summary>
|
||||
private double hpMultiplier = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The cumulative combo portion of the score.
|
||||
/// </summary>
|
||||
private double comboScore => combo_portion_max * comboPortion / maxComboPortion;
|
||||
|
||||
/// <summary>
|
||||
/// The cumulative accuracy portion of the score.
|
||||
/// </summary>
|
||||
private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 4) * totalHits / maxTotalHits;
|
||||
|
||||
/// <summary>
|
||||
/// The cumulative bonus score.
|
||||
/// This is added on top of <see cref="max_score"/>, thus the total score can exceed <see cref="max_score"/>.
|
||||
/// </summary>
|
||||
private double bonusScore;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="comboPortion"/> achieved by a perfect playthrough.
|
||||
/// </summary>
|
||||
private double maxComboPortion;
|
||||
|
||||
/// <summary>
|
||||
/// The portion of the score dedicated to combo.
|
||||
/// </summary>
|
||||
private double comboPortion;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="totalHits"/> achieved by a perfect playthrough.
|
||||
/// </summary>
|
||||
private int maxTotalHits;
|
||||
|
||||
/// <summary>
|
||||
/// The total hits.
|
||||
/// </summary>
|
||||
private int totalHits;
|
||||
|
||||
public ManiaScoreProcessor()
|
||||
{
|
||||
}
|
||||
@ -19,8 +161,124 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ComputeTargets(Beatmap<ManiaHitObject> beatmap)
|
||||
{
|
||||
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty;
|
||||
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
|
||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
||||
|
||||
while (true)
|
||||
{
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
var holdNote = obj as HoldNote;
|
||||
|
||||
if (obj is Note)
|
||||
{
|
||||
AddJudgement(new ManiaJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaHitResult.Perfect
|
||||
});
|
||||
}
|
||||
else if (holdNote != null)
|
||||
{
|
||||
// Head
|
||||
AddJudgement(new ManiaJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
|
||||
});
|
||||
|
||||
// Ticks
|
||||
int tickCount = holdNote.Ticks.Count();
|
||||
for (int i = 0; i < tickCount; i++)
|
||||
{
|
||||
AddJudgement(new HoldNoteTickJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaJudgement.MAX_HIT_RESULT,
|
||||
});
|
||||
}
|
||||
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
{
|
||||
Result = HitResult.Hit,
|
||||
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!HasFailed)
|
||||
break;
|
||||
|
||||
hpMultiplier *= 1.01;
|
||||
hpMissMultiplier *= 0.98;
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
maxTotalHits = totalHits;
|
||||
maxComboPortion = comboPortion;
|
||||
}
|
||||
|
||||
protected override void OnNewJudgement(ManiaJudgement judgement)
|
||||
{
|
||||
bool isTick = judgement is HoldNoteTickJudgement;
|
||||
|
||||
if (!isTick)
|
||||
totalHits++;
|
||||
|
||||
switch (judgement.Result)
|
||||
{
|
||||
case HitResult.Miss:
|
||||
Health.Value += hpMissMultiplier * hp_increase_miss;
|
||||
break;
|
||||
case HitResult.Hit:
|
||||
if (isTick)
|
||||
{
|
||||
Health.Value += hpMultiplier * hp_increase_tick;
|
||||
bonusScore += judgement.ResultValueForScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (judgement.ManiaResult)
|
||||
{
|
||||
case ManiaHitResult.Bad:
|
||||
Health.Value += hpMultiplier * hp_increase_bad;
|
||||
break;
|
||||
case ManiaHitResult.Ok:
|
||||
Health.Value += hpMultiplier * hp_increase_ok;
|
||||
break;
|
||||
case ManiaHitResult.Good:
|
||||
Health.Value += hpMultiplier * hp_increase_good;
|
||||
break;
|
||||
case ManiaHitResult.Great:
|
||||
Health.Value += hpMultiplier * hp_increase_great;
|
||||
break;
|
||||
case ManiaHitResult.Perfect:
|
||||
Health.Value += hpMultiplier * hp_increase_perfect;
|
||||
break;
|
||||
}
|
||||
|
||||
// A factor that is applied to make higher combos more relevant
|
||||
double comboRelevance = Math.Min(Math.Max(0.5, Math.Log(Combo.Value, combo_base)), Math.Log(combo_relevance_cap, combo_base));
|
||||
comboPortion += judgement.ResultValueForScore * comboRelevance;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int scoreForAccuracy = 0;
|
||||
int maxScoreForAccuracy = 0;
|
||||
|
||||
foreach (var j in Judgements)
|
||||
{
|
||||
scoreForAccuracy += j.ResultValueForAccuracy;
|
||||
maxScoreForAccuracy += j.MaxResultValueForAccuracy;
|
||||
}
|
||||
|
||||
Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
|
||||
TotalScore.Value = comboScore + accuracyScore + bonusScore;
|
||||
}
|
||||
|
||||
protected override void Reset()
|
||||
@ -28,6 +286,10 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
base.Reset();
|
||||
|
||||
Health.Value = 1;
|
||||
|
||||
bonusScore = 0;
|
||||
comboPortion = 0;
|
||||
totalHits = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Timing
|
||||
{
|
||||
@ -128,6 +129,8 @@ namespace osu.Game.Rulesets.Mania.Timing
|
||||
/// </summary>
|
||||
private class AutoTimeRelativeContainer : Container
|
||||
{
|
||||
protected override IComparer<Drawable> DepthComparer => new HitObjectReverseStartTimeComparer();
|
||||
|
||||
public override void InvalidateFromChild(Invalidation invalidation)
|
||||
{
|
||||
// We only want to re-compute our size when a child's size or position has changed
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
@ -188,7 +187,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject) => ControlPointContainer.Add(hitObject);
|
||||
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject)
|
||||
{
|
||||
hitObject.AccentColour = AccentColour;
|
||||
ControlPointContainer.Add(hitObject);
|
||||
}
|
||||
|
||||
private bool onKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
|
@ -6,9 +6,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Beatmaps;
|
||||
@ -85,6 +87,34 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var maniaPlayfield = (ManiaPlayfield)Playfield;
|
||||
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
SortedList<TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||
for (int i = 0; i < timingPoints.Count; i++)
|
||||
{
|
||||
TimingControlPoint point = timingPoints[i];
|
||||
|
||||
// Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
|
||||
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
|
||||
|
||||
int index = 0;
|
||||
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
|
||||
{
|
||||
maniaPlayfield.Add(new DrawableBarLine(new BarLine
|
||||
{
|
||||
StartTime = t,
|
||||
ControlPoint = point,
|
||||
BeatIndex = index
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||
|
@ -21,6 +21,7 @@ using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
@ -57,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private readonly FlowContainer<Column> columns;
|
||||
public IEnumerable<Column> Columns => columns.Children;
|
||||
|
||||
private readonly ControlPointContainer barlineContainer;
|
||||
private readonly ControlPointContainer barLineContainer;
|
||||
|
||||
private List<Color4> normalColumnColours = new List<Color4>();
|
||||
private Color4 specialColumnColour;
|
||||
@ -77,35 +78,51 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columns = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
Name = "Masked elements",
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black
|
||||
},
|
||||
columns = new FillFlowContainer<Column>
|
||||
{
|
||||
Name = "Columns",
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||
Spacing = new Vector2(1, 0)
|
||||
}
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
|
||||
Children = new[]
|
||||
{
|
||||
barlineContainer = new ControlPointContainer(timingChanges)
|
||||
barLineContainer = new ControlPointContainer(timingChanges)
|
||||
{
|
||||
Name = "Bar lines",
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Y
|
||||
// Width is set in the Update method
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,6 +207,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h);
|
||||
public void Add(DrawableBarLine barline) => barLineContainer.Add(barline);
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
@ -224,7 +242,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
|
||||
|
||||
barlineContainer.TimeSpan = value;
|
||||
barLineContainer.TimeSpan = value;
|
||||
Columns.ForEach(c => c.ControlPointContainer.TimeSpan = value);
|
||||
}
|
||||
}
|
||||
@ -234,6 +252,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||
barLineContainer.Width = columns.Width;
|
||||
}
|
||||
|
||||
private class TransformTimeSpan : Transform<double>
|
||||
{
|
||||
public override double CurrentValue
|
||||
|
@ -57,17 +57,23 @@
|
||||
<Compile Include="Beatmaps\Patterns\Pattern.cs" />
|
||||
<Compile Include="MathUtils\FastRandom.cs" />
|
||||
<Compile Include="Judgements\HitWindows.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTailJudgement.cs" />
|
||||
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
|
||||
<Compile Include="Judgements\ManiaHitResult.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableBarLine.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableHoldNoteTick.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableNote.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\BodyPiece.cs" />
|
||||
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
|
||||
<Compile Include="Objects\Types\IHasColumn.cs" />
|
||||
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
|
||||
<Compile Include="Objects\BarLine.cs" />
|
||||
<Compile Include="Objects\HoldNote.cs" />
|
||||
<Compile Include="Objects\HoldNoteTick.cs" />
|
||||
<Compile Include="Objects\ManiaHitObject.cs" />
|
||||
<Compile Include="Objects\Note.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -24,6 +24,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
|
||||
SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));
|
||||
|
||||
// spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
|
||||
SpinsRequired = (int)(SpinsRequired * 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -428,6 +428,9 @@ namespace osu.Game.Beatmaps.Formats
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
|
||||
}
|
||||
|
||||
internal enum LegacySampleBank
|
||||
|
@ -1,20 +1,36 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.MathUtils;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using System;
|
||||
using osu.Framework.Graphics.OpenGL;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Batches;
|
||||
using osu.Framework.Lists;
|
||||
|
||||
namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
public class Triangles : Container<Triangle>
|
||||
public class Triangles : Drawable
|
||||
{
|
||||
private const float triangle_size = 100;
|
||||
private const float base_velocity = 50;
|
||||
|
||||
/// <summary>
|
||||
/// How many screen-space pixels are smoothed over.
|
||||
/// Same behavior as Sprite's EdgeSmoothness.
|
||||
/// </summary>
|
||||
private const float edge_smoothness = 1;
|
||||
|
||||
public override bool HandleInput => false;
|
||||
|
||||
public Color4 ColourLight = Color4.White;
|
||||
@ -49,6 +65,28 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
/// </summary>
|
||||
public float Velocity = 1;
|
||||
|
||||
private readonly SortedList<TriangleParticle> parts = new SortedList<TriangleParticle>(Comparer<TriangleParticle>.Default);
|
||||
|
||||
private Shader shader;
|
||||
private readonly Texture texture;
|
||||
|
||||
public Triangles()
|
||||
{
|
||||
texture = Texture.WhitePixel;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ShaderManager shaders)
|
||||
{
|
||||
shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
addTriangles(true);
|
||||
}
|
||||
|
||||
public float TriangleScale
|
||||
{
|
||||
get { return triangleScale; }
|
||||
@ -57,42 +95,74 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
float change = value / triangleScale;
|
||||
triangleScale = value;
|
||||
|
||||
if (change != 1)
|
||||
Children.ForEach(t => t.Scale *= change);
|
||||
for (int i = 0; i < parts.Count; i++)
|
||||
{
|
||||
TriangleParticle newParticle = parts[i];
|
||||
newParticle.Scale *= change;
|
||||
parts[i] = newParticle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
addTriangles(true);
|
||||
}
|
||||
|
||||
private int aimTriangleCount => (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Invalidate(Invalidation.DrawNode, shallPropagate: false);
|
||||
|
||||
if (CreateNewTriangles)
|
||||
addTriangles(false);
|
||||
|
||||
float adjustedAlpha = HideAlphaDiscrepancies ?
|
||||
// Cubically scale alpha to make it drop off more sharply.
|
||||
(float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) :
|
||||
1;
|
||||
|
||||
foreach (var t in Children)
|
||||
{
|
||||
t.Alpha = adjustedAlpha;
|
||||
t.Position -= new Vector2(0, (float)(t.Scale.X * (50 / DrawHeight) * (Time.Elapsed / 950)) / triangleScale * Velocity);
|
||||
if (ExpireOffScreenTriangles && t.DrawPosition.Y + t.DrawSize.Y * t.Scale.Y < 0)
|
||||
t.Expire();
|
||||
}
|
||||
float elapsedSeconds = (float)Time.Elapsed / 1000;
|
||||
// Since position is relative, the velocity needs to scale inversely with DrawHeight.
|
||||
// Since we will later multiply by the scale of individual triangles we normalize by
|
||||
// dividing by triangleScale.
|
||||
float movedDistance = -elapsedSeconds * Velocity * base_velocity / (DrawHeight * triangleScale);
|
||||
|
||||
if (CreateNewTriangles)
|
||||
addTriangles(false);
|
||||
for (int i = 0; i < parts.Count; i++)
|
||||
{
|
||||
TriangleParticle newParticle = parts[i];
|
||||
|
||||
// Scale moved distance by the size of the triangle. Smaller triangles should move more slowly.
|
||||
newParticle.Position.Y += parts[i].Scale * movedDistance;
|
||||
newParticle.Colour.A = adjustedAlpha;
|
||||
|
||||
parts[i] = newParticle;
|
||||
|
||||
float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * 0.866f / DrawHeight;
|
||||
if (bottomPos < 0)
|
||||
parts.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Triangle CreateTriangle()
|
||||
private void addTriangles(bool randomY)
|
||||
{
|
||||
int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
|
||||
|
||||
for (int i = 0; i < aimTriangleCount - parts.Count; i++)
|
||||
parts.Add(createTriangle(randomY));
|
||||
}
|
||||
|
||||
private TriangleParticle createTriangle(bool randomY)
|
||||
{
|
||||
TriangleParticle particle = CreateTriangle();
|
||||
|
||||
particle.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() : 1);
|
||||
particle.Colour = CreateTriangleShade();
|
||||
|
||||
return particle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a triangle particle with a random scale.
|
||||
/// </summary>
|
||||
/// <returns>The triangle particle.</returns>
|
||||
protected virtual TriangleParticle CreateTriangle()
|
||||
{
|
||||
const float std_dev = 0.16f;
|
||||
const float mean = 0.5f;
|
||||
@ -102,32 +172,108 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1)
|
||||
var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2)
|
||||
|
||||
const float size = 100;
|
||||
|
||||
return new EquilateralTriangle
|
||||
{
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Size = new Vector2(size),
|
||||
Scale = new Vector2(scale),
|
||||
EdgeSmoothness = new Vector2(1),
|
||||
Colour = GetTriangleShade(),
|
||||
Depth = scale,
|
||||
};
|
||||
return new TriangleParticle { Scale = scale };
|
||||
}
|
||||
|
||||
protected virtual Color4 GetTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1);
|
||||
/// <summary>
|
||||
/// Creates a shade of colour for the triangles.
|
||||
/// </summary>
|
||||
/// <returns>The colour.</returns>
|
||||
protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1);
|
||||
|
||||
private void addTriangles(bool randomY)
|
||||
protected override DrawNode CreateDrawNode() => new TrianglesDrawNode();
|
||||
|
||||
private readonly TrianglesDrawNodeSharedData sharedData = new TrianglesDrawNodeSharedData();
|
||||
protected override void ApplyDrawNode(DrawNode node)
|
||||
{
|
||||
int addCount = aimTriangleCount - Children.Count();
|
||||
for (int i = 0; i < addCount; i++)
|
||||
base.ApplyDrawNode(node);
|
||||
|
||||
var trianglesNode = (TrianglesDrawNode)node;
|
||||
|
||||
trianglesNode.Shader = shader;
|
||||
trianglesNode.Texture = texture;
|
||||
trianglesNode.Size = DrawSize;
|
||||
trianglesNode.Shared = sharedData;
|
||||
|
||||
trianglesNode.Parts.Clear();
|
||||
trianglesNode.Parts.AddRange(parts);
|
||||
}
|
||||
|
||||
private class TrianglesDrawNodeSharedData
|
||||
{
|
||||
public readonly LinearBatch<TexturedVertex2D> VertexBatch = new LinearBatch<TexturedVertex2D>(100 * 3, 10, PrimitiveType.Triangles);
|
||||
}
|
||||
|
||||
private class TrianglesDrawNode : DrawNode
|
||||
{
|
||||
public Shader Shader;
|
||||
public Texture Texture;
|
||||
|
||||
public TrianglesDrawNodeSharedData Shared;
|
||||
|
||||
public readonly List<TriangleParticle> Parts = new List<TriangleParticle>();
|
||||
public Vector2 Size;
|
||||
|
||||
public override void Draw(Action<TexturedVertex2D> vertexAction)
|
||||
{
|
||||
var sprite = CreateTriangle();
|
||||
float triangleHeight = sprite.DrawHeight / DrawHeight;
|
||||
sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1);
|
||||
Add(sprite);
|
||||
base.Draw(vertexAction);
|
||||
|
||||
Shader.Bind();
|
||||
Texture.TextureGL.Bind();
|
||||
|
||||
Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy;
|
||||
|
||||
foreach (TriangleParticle particle in Parts)
|
||||
{
|
||||
var offset = triangle_size * new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f);
|
||||
var size = new Vector2(2 * offset.X, offset.Y);
|
||||
|
||||
var triangle = new Triangle(
|
||||
particle.Position * Size * DrawInfo.Matrix,
|
||||
(particle.Position * Size + offset) * DrawInfo.Matrix,
|
||||
(particle.Position * Size + new Vector2(-offset.X, offset.Y)) * DrawInfo.Matrix
|
||||
);
|
||||
|
||||
ColourInfo colourInfo = DrawInfo.Colour;
|
||||
colourInfo.ApplyChild(particle.Colour);
|
||||
|
||||
Texture.DrawTriangle(
|
||||
triangle,
|
||||
colourInfo,
|
||||
null,
|
||||
Shared.VertexBatch.Add,
|
||||
Vector2.Divide(localInflationAmount, size));
|
||||
}
|
||||
|
||||
Shader.Unbind();
|
||||
}
|
||||
}
|
||||
|
||||
protected struct TriangleParticle : IComparable<TriangleParticle>
|
||||
{
|
||||
/// <summary>
|
||||
/// The position of the top vertex of the triangle.
|
||||
/// </summary>
|
||||
public Vector2 Position;
|
||||
|
||||
/// <summary>
|
||||
/// The colour of the triangle.
|
||||
/// </summary>
|
||||
public Color4 Colour;
|
||||
|
||||
/// <summary>
|
||||
/// The scale of the triangle.
|
||||
/// </summary>
|
||||
public float Scale;
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="TriangleParticle"/>s. This is a reverse comparer because when the
|
||||
/// triangles are added to the particles list, they should be drawn from largest to smallest
|
||||
/// such that the smaller triangles appear on top.
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
Normal file
84
osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class BreadcrumbControl<T> : OsuTabControl<T>
|
||||
{
|
||||
private const float padding = 10;
|
||||
|
||||
protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value);
|
||||
|
||||
public BreadcrumbControl()
|
||||
{
|
||||
Height = 26;
|
||||
TabContainer.Spacing = new Vector2(padding, 0f);
|
||||
Current.ValueChanged += tab =>
|
||||
{
|
||||
foreach (var t in TabContainer.Children.OfType<BreadcrumbTabItem>())
|
||||
{
|
||||
var tIndex = TabContainer.IndexOf(t);
|
||||
var tabIndex = TabContainer.IndexOf(TabMap[tab]);
|
||||
|
||||
t.State = tIndex < tabIndex ? Visibility.Hidden : Visibility.Visible;
|
||||
t.Chevron.FadeTo(tIndex <= tabIndex ? 0f : 1f, 500, EasingTypes.OutQuint);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class BreadcrumbTabItem : OsuTabItem, IStateful<Visibility>
|
||||
{
|
||||
public readonly TextAwesome Chevron;
|
||||
|
||||
//don't allow clicking between transitions and don't make the chevron clickable
|
||||
protected override bool InternalContains(Vector2 screenSpacePos) => Alpha == 1f && Text.Contains(screenSpacePos);
|
||||
public override bool HandleInput => State == Visibility.Visible;
|
||||
|
||||
private Visibility state;
|
||||
public Visibility State
|
||||
{
|
||||
get { return state; }
|
||||
set
|
||||
{
|
||||
if (value == state) return;
|
||||
state = value;
|
||||
|
||||
const float transition_duration = 500;
|
||||
|
||||
if (State == Visibility.Visible)
|
||||
{
|
||||
FadeIn(transition_duration, EasingTypes.OutQuint);
|
||||
ScaleTo(new Vector2(1f), transition_duration, EasingTypes.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
FadeOut(transition_duration, EasingTypes.OutQuint);
|
||||
ScaleTo(new Vector2(0.8f, 1f), transition_duration, EasingTypes.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BreadcrumbTabItem(T value) : base(value)
|
||||
{
|
||||
Text.TextSize = 16;
|
||||
Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width
|
||||
Add(Chevron = new TextAwesome
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreLeft,
|
||||
TextSize = 12,
|
||||
Icon = FontAwesome.fa_chevron_right,
|
||||
Margin = new MarginPadding { Left = padding },
|
||||
Alpha = 0f,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Input;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@ -23,11 +24,19 @@ namespace osu.Game.Graphics.UserInterface
|
||||
set
|
||||
{
|
||||
focus = value;
|
||||
if (!focus)
|
||||
TriggerFocusLost();
|
||||
if (!focus && HasFocus)
|
||||
inputManager.ChangeFocus(null);
|
||||
}
|
||||
}
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(UserInputManager inputManager)
|
||||
{
|
||||
this.inputManager = inputManager;
|
||||
}
|
||||
|
||||
protected override bool OnFocus(InputState state)
|
||||
{
|
||||
var result = base.OnFocus(state);
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected override DropdownMenuItem<T> CreateMenuItem(string text, T value) => new OsuDropdownMenuItem(text, value) { AccentColour = AccentColour };
|
||||
|
||||
private class OsuDropdownMenuItem : DropdownMenuItem<T>
|
||||
public class OsuDropdownMenuItem : DropdownMenuItem<T>
|
||||
{
|
||||
public OsuDropdownMenuItem(string text, T current) : base(text, current)
|
||||
{
|
||||
@ -60,7 +60,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
chevron = new TextAwesome
|
||||
Chevron = new TextAwesome
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
Icon = FontAwesome.fa_chevron_right,
|
||||
@ -84,12 +84,12 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
private Color4? accentColour;
|
||||
|
||||
private readonly TextAwesome chevron;
|
||||
protected readonly TextAwesome Chevron;
|
||||
|
||||
protected override void FormatForeground(bool hover = false)
|
||||
{
|
||||
base.FormatForeground(hover);
|
||||
chevron.Alpha = hover ? 1 : 0;
|
||||
Chevron.Alpha = hover ? 1 : 0;
|
||||
}
|
||||
|
||||
public Color4 AccentColour
|
||||
@ -115,11 +115,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public class OsuDropdownHeader : DropdownHeader
|
||||
{
|
||||
private readonly SpriteText label;
|
||||
protected readonly SpriteText Text;
|
||||
protected override string Label
|
||||
{
|
||||
get { return label.Text; }
|
||||
set { label.Text = value; }
|
||||
get { return Text.Text; }
|
||||
set { Text.Text = value; }
|
||||
}
|
||||
|
||||
protected readonly TextAwesome Icon;
|
||||
@ -146,7 +146,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
Foreground.Children = new Drawable[]
|
||||
{
|
||||
label = new OsuSpriteText
|
||||
Text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
|
@ -5,18 +5,21 @@ using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Framework.Audio.Track;
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class TwoLayerButton : ClickableContainer
|
||||
{
|
||||
private readonly TextAwesome icon;
|
||||
private readonly BouncingIcon bouncingIcon;
|
||||
|
||||
public Box IconLayer;
|
||||
public Box TextLayer;
|
||||
@ -95,11 +98,10 @@ namespace osu.Game.Graphics.UserInterface
|
||||
},
|
||||
}
|
||||
},
|
||||
icon = new TextAwesome
|
||||
bouncingIcon = new BouncingIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = 25,
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -146,7 +148,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
set
|
||||
{
|
||||
icon.Icon = value;
|
||||
bouncingIcon.Icon = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,58 +164,20 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
icon.ClearTransforms();
|
||||
|
||||
ResizeTo(SIZE_EXTENDED, transform_time, EasingTypes.OutElastic);
|
||||
|
||||
int duration = 0; //(int)(Game.Audio.BeatLength / 2);
|
||||
if (duration == 0) duration = pulse_length;
|
||||
|
||||
IconLayer.FadeColour(HoverColour, transform_time, EasingTypes.OutElastic);
|
||||
|
||||
const 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
|
||||
});
|
||||
bouncingIcon.ScaleTo(1.1f, transform_time, EasingTypes.OutElastic);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
icon.ClearTransforms();
|
||||
|
||||
ResizeTo(SIZE_RETRACTED, transform_time, EasingTypes.OutElastic);
|
||||
|
||||
IconLayer.FadeColour(TextLayer.Colour, transform_time, EasingTypes.OutElastic);
|
||||
|
||||
int duration = 0; //(int)(Game.Audio.BeatLength);
|
||||
if (duration == 0) duration = pulse_length * 2;
|
||||
|
||||
const 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
|
||||
});
|
||||
bouncingIcon.ScaleTo(1, transform_time, EasingTypes.OutElastic);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
@ -239,5 +203,45 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
return base.OnClick(state);
|
||||
}
|
||||
|
||||
private class BouncingIcon : BeatSyncedContainer
|
||||
{
|
||||
private const double beat_in_time = 60;
|
||||
|
||||
private readonly TextAwesome icon;
|
||||
|
||||
public FontAwesome Icon { set { icon.Icon = value; } }
|
||||
|
||||
public BouncingIcon()
|
||||
{
|
||||
EarlyActivationMilliseconds = beat_in_time;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
icon = new TextAwesome
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = 25
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
|
||||
{
|
||||
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
||||
|
||||
var beatLength = timingPoint.BeatLength;
|
||||
|
||||
float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum);
|
||||
|
||||
if (beatIndex < 0) return;
|
||||
|
||||
icon.ScaleTo(1 - 0.1f * amplitudeAdjust, beat_in_time, EasingTypes.Out);
|
||||
using (icon.BeginDelayedSequence(beat_in_time))
|
||||
icon.ScaleTo(1, beatLength * 2, EasingTypes.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -74,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
||||
return;
|
||||
}
|
||||
|
||||
volumeMeterMaster.TriggerWheel(state);
|
||||
volumeMeterMaster.TriggerOnWheel(state);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
17
osu.Game/Online/Multiplayer/Room.cs
Normal file
17
osu.Game/Online/Multiplayer/Room.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
public class Room
|
||||
{
|
||||
public Bindable<string> Name = new Bindable<string>();
|
||||
public Bindable<User> Host = new Bindable<User>();
|
||||
public Bindable<RoomStatus> Status = new Bindable<RoomStatus>();
|
||||
public Bindable<BeatmapMetadata> Beatmap = new Bindable<BeatmapMetadata>();
|
||||
}
|
||||
}
|
26
osu.Game/Online/Multiplayer/RoomStatus.cs
Normal file
26
osu.Game/Online/Multiplayer/RoomStatus.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Online.Multiplayer
|
||||
{
|
||||
public abstract class RoomStatus
|
||||
{
|
||||
public abstract string Message { get; }
|
||||
public abstract Color4 GetAppropriateColour(OsuColour colours);
|
||||
}
|
||||
|
||||
public class RoomStatusOpen : RoomStatus
|
||||
{
|
||||
public override string Message => @"Welcoming Players";
|
||||
public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight;
|
||||
}
|
||||
|
||||
public class RoomStatusPlaying : RoomStatus
|
||||
{
|
||||
public override string Message => @"Now Playing";
|
||||
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Purple;
|
||||
}
|
||||
}
|
@ -255,6 +255,9 @@ namespace osu.Game
|
||||
settings.ToggleVisibility();
|
||||
return true;
|
||||
case Key.D:
|
||||
if (state.Keyboard.ShiftPressed || state.Keyboard.AltPressed)
|
||||
return false;
|
||||
|
||||
direct.ToggleVisibility();
|
||||
return true;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Chat;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Configuration;
|
||||
|
||||
namespace osu.Game.Overlays.Chat
|
||||
{
|
||||
@ -23,6 +24,8 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
private const float shear_width = 10;
|
||||
|
||||
public readonly Bindable<bool> ChannelSelectorActive = new Bindable<bool>();
|
||||
|
||||
public ChatTabControl()
|
||||
{
|
||||
TabContainer.Margin = new MarginPadding { Left = 50 };
|
||||
@ -37,6 +40,8 @@ namespace osu.Game.Overlays.Chat
|
||||
TextSize = 20,
|
||||
Padding = new MarginPadding(10),
|
||||
});
|
||||
|
||||
AddTabItem(new ChannelTabItem.ChannelSelectorTabItem(new Channel { Name = "+" }, ChannelSelectorActive));
|
||||
}
|
||||
|
||||
private class ChannelTabItem : TabItem<Channel>
|
||||
@ -49,6 +54,7 @@ namespace osu.Game.Overlays.Chat
|
||||
private readonly SpriteText textBold;
|
||||
private readonly Box box;
|
||||
private readonly Box highlightBox;
|
||||
private readonly TextAwesome icon;
|
||||
|
||||
public override bool Active
|
||||
{
|
||||
@ -114,6 +120,11 @@ namespace osu.Game.Overlays.Chat
|
||||
backgroundHover = colours.Gray7;
|
||||
|
||||
highlightBox.Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updateState();
|
||||
}
|
||||
@ -159,7 +170,7 @@ namespace osu.Game.Overlays.Chat
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TextAwesome
|
||||
icon = new TextAwesome
|
||||
{
|
||||
Icon = FontAwesome.fa_hashtag,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
@ -191,6 +202,40 @@ namespace osu.Game.Overlays.Chat
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public class ChannelSelectorTabItem : ChannelTabItem
|
||||
{
|
||||
public override bool Active
|
||||
{
|
||||
get { return base.Active; }
|
||||
set
|
||||
{
|
||||
activeBindable.Value = value;
|
||||
base.Active = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Bindable<bool> activeBindable;
|
||||
|
||||
public ChannelSelectorTabItem(Channel value, Bindable<bool> active) : base(value)
|
||||
{
|
||||
activeBindable = active;
|
||||
Depth = float.MaxValue;
|
||||
Width = 45;
|
||||
|
||||
icon.Alpha = 0;
|
||||
|
||||
text.TextSize = 45;
|
||||
textBold.TextSize = 45;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private new void load(OsuColour colour)
|
||||
{
|
||||
backgroundInactive = colour.Gray2;
|
||||
backgroundActive = colour.Gray3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,17 +135,22 @@ namespace osu.Game.Overlays
|
||||
channelTabs.Current.ValueChanged += newChannel => CurrentChannel = newChannel;
|
||||
}
|
||||
|
||||
private double startDragChatHeight;
|
||||
|
||||
protected override bool OnDragStart(InputState state)
|
||||
{
|
||||
if (channelTabs.Hovering)
|
||||
return true;
|
||||
if (!channelTabs.Hovering)
|
||||
return base.OnDragStart(state);
|
||||
|
||||
return base.OnDragStart(state);
|
||||
startDragChatHeight = chatHeight.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
chatHeight.Value = Height - state.Mouse.Delta.Y / Parent.DrawSize.Y;
|
||||
Trace.Assert(state.Mouse.PositionMouseDown != null);
|
||||
|
||||
chatHeight.Value = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
|
||||
return base.OnDrag(state);
|
||||
}
|
||||
|
||||
@ -165,7 +170,7 @@ namespace osu.Game.Overlays
|
||||
protected override bool OnFocus(InputState state)
|
||||
{
|
||||
//this is necessary as inputTextBox is masked away and therefore can't get focus :(
|
||||
inputTextBox.TriggerFocus();
|
||||
InputManager.ChangeFocus(inputTextBox);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -255,6 +260,8 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
if (currentChannel == value) return;
|
||||
|
||||
if (channelTabs.ChannelSelectorActive) return;
|
||||
|
||||
if (currentChannel != null)
|
||||
currentChannelContainer.Clear(false);
|
||||
|
||||
|
@ -71,7 +71,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
private void pressButtonAtIndex(int index)
|
||||
{
|
||||
if (index < Buttons.Count())
|
||||
Buttons.Skip(index).First().TriggerClick();
|
||||
Buttons.Skip(index).First().TriggerOnClick();
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
@ -80,7 +80,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
|
||||
if (args.Key == Key.Enter)
|
||||
{
|
||||
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerClick();
|
||||
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
protected override bool OnFocus(InputState state)
|
||||
{
|
||||
filter.Search.TriggerFocus();
|
||||
InputManager.ChangeFocus(filter.Search);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Overlays
|
||||
settingsSection.Bounding = true;
|
||||
FadeIn(transition_time, EasingTypes.OutQuint);
|
||||
|
||||
settingsSection.TriggerFocus();
|
||||
InputManager.ChangeFocus(settingsSection);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
|
@ -27,6 +27,7 @@ namespace osu.Game.Overlays.Mods
|
||||
public class ModButton : ModButtonEmpty, IHasTooltip
|
||||
{
|
||||
private ModIcon foregroundIcon;
|
||||
private ModIcon backgroundIcon;
|
||||
private readonly SpriteText text;
|
||||
private readonly Container<ModIcon> iconsContainer;
|
||||
private SampleChannel sampleOn, sampleOff;
|
||||
@ -35,38 +36,67 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
|
||||
|
||||
private int _selectedIndex = -1;
|
||||
private int selectedIndex
|
||||
private const EasingTypes mod_switch_easing = EasingTypes.InOutSine;
|
||||
private const double mod_switch_duration = 120;
|
||||
|
||||
// A selected index of -1 means not selected.
|
||||
private int selectedIndex = -1;
|
||||
|
||||
protected int SelectedIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return _selectedIndex;
|
||||
return selectedIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == _selectedIndex) return;
|
||||
_selectedIndex = value;
|
||||
if (value == selectedIndex) return;
|
||||
|
||||
int direction = value < selectedIndex ? -1 : 1;
|
||||
bool beforeSelected = Selected;
|
||||
|
||||
Mod modBefore = SelectedMod ?? Mods[0];
|
||||
|
||||
if (value >= Mods.Length)
|
||||
selectedIndex = -1;
|
||||
else if (value < -1)
|
||||
selectedIndex = Mods.Length - 1;
|
||||
else
|
||||
selectedIndex = value;
|
||||
|
||||
Mod modAfter = SelectedMod ?? Mods[0];
|
||||
|
||||
if (beforeSelected != Selected)
|
||||
{
|
||||
_selectedIndex = -1;
|
||||
}
|
||||
else if (value <= -2)
|
||||
{
|
||||
_selectedIndex = Mods.Length - 1;
|
||||
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
|
||||
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
|
||||
}
|
||||
|
||||
if (modBefore != modAfter)
|
||||
{
|
||||
const float rotate_angle = 16;
|
||||
|
||||
foregroundIcon.RotateTo(rotate_angle * direction, mod_switch_duration, mod_switch_easing);
|
||||
backgroundIcon.RotateTo(-rotate_angle * direction, mod_switch_duration, mod_switch_easing);
|
||||
|
||||
backgroundIcon.Icon = modAfter.Icon;
|
||||
using (iconsContainer.BeginDelayedSequence(mod_switch_duration, true))
|
||||
{
|
||||
foregroundIcon.RotateTo(-rotate_angle * direction);
|
||||
foregroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
||||
|
||||
backgroundIcon.RotateTo(rotate_angle * direction);
|
||||
backgroundIcon.RotateTo(0f, mod_switch_duration, mod_switch_easing);
|
||||
|
||||
iconsContainer.Schedule(() => displayMod(modAfter));
|
||||
}
|
||||
}
|
||||
|
||||
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
|
||||
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
|
||||
foregroundIcon.Highlighted = Selected;
|
||||
|
||||
if (mod != null)
|
||||
displayMod(SelectedMod ?? Mods[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Selected => selectedIndex != -1;
|
||||
|
||||
public bool Selected => SelectedIndex != -1;
|
||||
|
||||
private Color4 selectedColour;
|
||||
public Color4 SelectedColour
|
||||
@ -117,7 +147,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
// the mods from Mod, only multiple if Mod is a MultiMod
|
||||
|
||||
public override Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
|
||||
public override Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
@ -142,23 +172,25 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
public void SelectNext()
|
||||
{
|
||||
(++selectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
(++SelectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
Action?.Invoke(SelectedMod);
|
||||
}
|
||||
|
||||
public void SelectPrevious()
|
||||
{
|
||||
(--selectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
(--SelectedIndex == -1 ? sampleOff : sampleOn).Play();
|
||||
Action?.Invoke(SelectedMod);
|
||||
}
|
||||
|
||||
public void Deselect()
|
||||
{
|
||||
selectedIndex = -1;
|
||||
SelectedIndex = -1;
|
||||
}
|
||||
|
||||
private void displayMod(Mod mod)
|
||||
{
|
||||
if (backgroundIcon != null)
|
||||
backgroundIcon.Icon = foregroundIcon.Icon;
|
||||
foregroundIcon.Icon = mod.Icon;
|
||||
text.Text = mod.Name;
|
||||
}
|
||||
@ -170,17 +202,17 @@ namespace osu.Game.Overlays.Mods
|
||||
{
|
||||
iconsContainer.Add(new[]
|
||||
{
|
||||
new ModIcon(Mods[0])
|
||||
backgroundIcon = new ModIcon(Mods[1])
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.BottomRight,
|
||||
Anchor = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Position = new Vector2(1.5f),
|
||||
},
|
||||
foregroundIcon = new ModIcon(Mods[0])
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.BottomRight,
|
||||
Anchor = Anchor.BottomRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Position = new Vector2(-1.5f),
|
||||
},
|
||||
|
@ -17,6 +17,7 @@ using osu.Game.Graphics;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Game.Overlays.Music
|
||||
{
|
||||
@ -35,10 +36,12 @@ namespace osu.Game.Overlays.Music
|
||||
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
|
||||
|
||||
public IEnumerable<BeatmapSetInfo> BeatmapSets;
|
||||
private InputManager inputManager;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours)
|
||||
private void load(OsuGameBase game, BeatmapDatabase beatmaps, OsuColour colours, UserInputManager inputManager)
|
||||
{
|
||||
this.inputManager = inputManager;
|
||||
this.beatmaps = beatmaps;
|
||||
trackManager = game.Audio.Track;
|
||||
|
||||
@ -100,7 +103,7 @@ namespace osu.Game.Overlays.Music
|
||||
protected override void PopIn()
|
||||
{
|
||||
filter.Search.HoldFocus = true;
|
||||
Schedule(() => filter.Search.TriggerFocus());
|
||||
Schedule(() => inputManager.ChangeFocus(filter.Search));
|
||||
|
||||
ResizeTo(new Vector2(1, playlist_height), transition_duration, EasingTypes.OutQuint);
|
||||
FadeIn(transition_duration, EasingTypes.OutQuint);
|
||||
|
@ -266,24 +266,27 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
progressBar.IsEnabled = beatmap != null;
|
||||
|
||||
bool audioEquals = beatmapBacking.Value?.BeatmapInfo?.AudioEquals(current?.BeatmapInfo) ?? false;
|
||||
TransformDirection direction = TransformDirection.None;
|
||||
|
||||
TransformDirection direction;
|
||||
|
||||
if (audioEquals)
|
||||
direction = TransformDirection.None;
|
||||
else if (queuedDirection.HasValue)
|
||||
if (current != null)
|
||||
{
|
||||
direction = queuedDirection.Value;
|
||||
queuedDirection = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//figure out the best direction based on order in playlist.
|
||||
var last = current == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
|
||||
var next = beatmapBacking.Value == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmapBacking.Value.BeatmapSetInfo.ID).Count();
|
||||
bool audioEquals = beatmapBacking.Value?.BeatmapInfo?.AudioEquals(current.BeatmapInfo) ?? false;
|
||||
|
||||
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
|
||||
if (audioEquals)
|
||||
direction = TransformDirection.None;
|
||||
else if (queuedDirection.HasValue)
|
||||
{
|
||||
direction = queuedDirection.Value;
|
||||
queuedDirection = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
//figure out the best direction based on order in playlist.
|
||||
var last = playlist.BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo.ID).Count();
|
||||
var next = beatmapBacking.Value == null ? -1 : playlist.BeatmapSets.TakeWhile(b => b.ID != beatmapBacking.Value.BeatmapSetInfo.ID).Count();
|
||||
|
||||
direction = last > next ? TransformDirection.Prev : TransformDirection.Next;
|
||||
}
|
||||
}
|
||||
|
||||
current = beatmapBacking.Value;
|
||||
|
@ -13,15 +13,23 @@ using osu.Game.Online.API;
|
||||
using OpenTK;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Users;
|
||||
using System.ComponentModel;
|
||||
using osu.Game.Graphics;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.General
|
||||
{
|
||||
public class LoginSettings : SettingsSubsection, IOnlineComponent
|
||||
public class LoginSettings : FillFlowContainer, IOnlineComponent
|
||||
{
|
||||
private bool bounding = true;
|
||||
private LoginForm form;
|
||||
private OsuColour colours;
|
||||
|
||||
protected override string Header => "Account";
|
||||
private UserPanel panel;
|
||||
private UserDropdown dropdown;
|
||||
|
||||
public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty;
|
||||
|
||||
@ -35,9 +43,21 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(APIAccess api)
|
||||
public LoginSettings()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Direction = FillDirection.Vertical;
|
||||
Spacing = new Vector2(0f, 5f);
|
||||
}
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuColour colours, APIAccess api, UserInputManager inputManager)
|
||||
{
|
||||
this.inputManager = inputManager;
|
||||
this.colours = colours;
|
||||
api?.Register(this);
|
||||
}
|
||||
|
||||
@ -50,6 +70,12 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
case APIState.Offline:
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "ACCOUNT",
|
||||
Margin = new MarginPadding { Bottom = 5 },
|
||||
Font = @"Exo2.0-Black",
|
||||
},
|
||||
form = new LoginForm()
|
||||
};
|
||||
break;
|
||||
@ -67,33 +93,82 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Connecting...",
|
||||
Margin = new MarginPadding { Top = 10, Bottom = 10 },
|
||||
},
|
||||
};
|
||||
break;
|
||||
case APIState.Online:
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new UserPanel(api.LocalUser.Value)
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Left = 20, Right = 20 },
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0f, 10f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "Signed in",
|
||||
TextSize = 18,
|
||||
Font = @"Exo2.0-Bold",
|
||||
Margin = new MarginPadding { Top = 5, Bottom = 5 },
|
||||
},
|
||||
},
|
||||
},
|
||||
panel = new UserPanel(api.LocalUser.Value) { RelativeSizeAxes = Axes.X },
|
||||
dropdown = new UserDropdown { RelativeSizeAxes = Axes.X },
|
||||
},
|
||||
},
|
||||
new OsuButton
|
||||
};
|
||||
|
||||
panel.Status.BindTo(api.LocalUser.Value.Status);
|
||||
|
||||
dropdown.Current.ValueChanged += newValue =>
|
||||
{
|
||||
switch (newValue)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Sign out",
|
||||
Action = api.Logout
|
||||
case UserAction.Online:
|
||||
api.LocalUser.Value.Status.Value = new UserStatusOnline();
|
||||
dropdown.StatusColour = colours.Green;
|
||||
break;
|
||||
case UserAction.DoNotDisturb:
|
||||
api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb();
|
||||
dropdown.StatusColour = colours.Red;
|
||||
break;
|
||||
case UserAction.AppearOffline:
|
||||
api.LocalUser.Value.Status.Value = new UserStatusOffline();
|
||||
dropdown.StatusColour = colours.Gray7;
|
||||
break;
|
||||
case UserAction.SignOut:
|
||||
api.Logout();
|
||||
break;
|
||||
}
|
||||
};
|
||||
dropdown.Current.TriggerChange();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
form?.TriggerFocus();
|
||||
if (form != null) inputManager.ChangeFocus(form);
|
||||
}
|
||||
|
||||
protected override bool OnFocus(InputState state)
|
||||
{
|
||||
form?.TriggerFocus();
|
||||
if (form != null) inputManager.ChangeFocus(form);
|
||||
return base.OnFocus(state);
|
||||
}
|
||||
|
||||
@ -102,6 +177,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
private TextBox username;
|
||||
private TextBox password;
|
||||
private APIAccess api;
|
||||
private InputManager inputManager;
|
||||
|
||||
private void performLogin()
|
||||
{
|
||||
@ -110,8 +186,9 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(APIAccess api, OsuConfigManager config)
|
||||
private void load(APIAccess api, OsuConfigManager config, UserInputManager inputManager)
|
||||
{
|
||||
this.inputManager = inputManager;
|
||||
this.api = api;
|
||||
Direction = FillDirection.Vertical;
|
||||
Spacing = new Vector2(0, 5);
|
||||
@ -160,16 +237,123 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
|
||||
protected override bool OnFocus(InputState state)
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(username.Text))
|
||||
username.TriggerFocus();
|
||||
else
|
||||
password.TriggerFocus();
|
||||
});
|
||||
|
||||
Schedule(() => { inputManager.ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); });
|
||||
return base.OnFocus(state);
|
||||
}
|
||||
}
|
||||
|
||||
private class UserDropdown : OsuEnumDropdown<UserAction>
|
||||
{
|
||||
protected override DropdownHeader CreateHeader() => new UserDropdownHeader { AccentColour = AccentColour };
|
||||
protected override Menu CreateMenu() => new UserDropdownMenu();
|
||||
protected override DropdownMenuItem<UserAction> CreateMenuItem(string text, UserAction value) => new UserDropdownMenuItem(text, value) { AccentColour = AccentColour };
|
||||
|
||||
public Color4 StatusColour
|
||||
{
|
||||
set
|
||||
{
|
||||
var h = Header as UserDropdownHeader;
|
||||
if (h == null) return;
|
||||
h.StatusColour = value;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
AccentColour = colours.Gray5;
|
||||
}
|
||||
|
||||
private class UserDropdownHeader : OsuDropdownHeader
|
||||
{
|
||||
public const float LABEL_LEFT_MARGIN = 20;
|
||||
|
||||
private readonly TextAwesome statusIcon;
|
||||
public Color4 StatusColour
|
||||
{
|
||||
set
|
||||
{
|
||||
statusIcon.FadeColour(value, 500, EasingTypes.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
public UserDropdownHeader()
|
||||
{
|
||||
Foreground.Padding = new MarginPadding { Left = 10, Right = 10 };
|
||||
Margin = new MarginPadding { Bottom = 5 };
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Radius = 4,
|
||||
};
|
||||
|
||||
Icon.TextSize = 14;
|
||||
Icon.Margin = new MarginPadding(0);
|
||||
|
||||
Foreground.Add(statusIcon = new TextAwesome
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Icon = FontAwesome.fa_circle_o,
|
||||
TextSize = 14,
|
||||
});
|
||||
|
||||
Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
BackgroundColour = colours.Gray3;
|
||||
}
|
||||
}
|
||||
|
||||
private class UserDropdownMenu : OsuMenu
|
||||
{
|
||||
public UserDropdownMenu()
|
||||
{
|
||||
Margin = new MarginPadding { Bottom = 5 };
|
||||
CornerRadius = 5;
|
||||
ItemsContainer.Padding = new MarginPadding(0);
|
||||
Masking = true;
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(0.25f),
|
||||
Radius = 4,
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Background.Colour = colours.Gray3;
|
||||
}
|
||||
}
|
||||
|
||||
private class UserDropdownMenuItem : OsuDropdownMenuItem
|
||||
{
|
||||
public UserDropdownMenuItem(string text, UserAction current) : base(text, current)
|
||||
{
|
||||
Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = UserDropdownHeader.LABEL_LEFT_MARGIN, Right = 5 };
|
||||
Chevron.Margin = new MarginPadding { Left = 2, Right = 3 };
|
||||
CornerRadius = 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum UserAction
|
||||
{
|
||||
Online,
|
||||
[Description(@"Do not disturb")]
|
||||
DoNotDisturb,
|
||||
[Description(@"Appear offline")]
|
||||
AppearOffline,
|
||||
[Description(@"Sign out")]
|
||||
SignOut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,12 +134,13 @@ namespace osu.Game.Overlays
|
||||
FadeTo(0, TRANSITION_LENGTH / 2);
|
||||
|
||||
searchTextBox.HoldFocus = false;
|
||||
searchTextBox.TriggerFocusLost();
|
||||
if (searchTextBox.HasFocus)
|
||||
InputManager.ChangeFocus(null);
|
||||
}
|
||||
|
||||
protected override bool OnFocus(InputState state)
|
||||
{
|
||||
searchTextBox.TriggerFocus(state);
|
||||
InputManager.ChangeFocus(searchTextBox);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -15,28 +15,51 @@ using System.Linq;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
public abstract class DrawableHitObject<TObject, TJudgement> : Container
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
public abstract class DrawableHitObject : Container
|
||||
{
|
||||
public event Action<DrawableHitObject<TObject, TJudgement>> OnJudgement;
|
||||
|
||||
public TObject HitObject;
|
||||
public readonly HitObject HitObject;
|
||||
|
||||
/// <summary>
|
||||
/// The colour used for various elements of this DrawableHitObject.
|
||||
/// </summary>
|
||||
public virtual Color4 AccentColour { get; set; }
|
||||
|
||||
protected DrawableHitObject(HitObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableHitObject<TObject> : DrawableHitObject
|
||||
where TObject : HitObject
|
||||
{
|
||||
public new readonly TObject HitObject;
|
||||
|
||||
protected DrawableHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableHitObject<TObject, TJudgement> : DrawableHitObject<TObject>
|
||||
where TObject : HitObject
|
||||
where TJudgement : Judgement
|
||||
{
|
||||
public event Action<DrawableHitObject<TObject, TJudgement>> OnJudgement;
|
||||
|
||||
public override bool HandleInput => Interactive;
|
||||
|
||||
public bool Interactive = true;
|
||||
|
||||
public TJudgement Judgement;
|
||||
|
||||
protected abstract TJudgement CreateJudgement();
|
||||
protected List<SampleChannel> Samples = new List<SampleChannel>();
|
||||
|
||||
protected abstract void UpdateState(ArmedState state);
|
||||
protected DrawableHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
private ArmedState state;
|
||||
public ArmedState State
|
||||
@ -59,8 +82,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected List<SampleChannel> Samples = new List<SampleChannel>();
|
||||
|
||||
protected void PlaySamples()
|
||||
{
|
||||
Samples.ForEach(s => s?.Play());
|
||||
@ -79,11 +100,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </summary>
|
||||
public bool Judged => (Judgement?.Result ?? HitResult.None) != HitResult.None && (NestedHitObjects?.All(h => h.Judged) ?? true);
|
||||
|
||||
protected DrawableHitObject(TObject hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a hit of this hitobject. Carries out judgement.
|
||||
/// </summary>
|
||||
@ -176,5 +192,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
h.OnJudgement += d => OnJudgement?.Invoke(d);
|
||||
nestedHitObjects.Add(h);
|
||||
}
|
||||
|
||||
protected abstract TJudgement CreateJudgement();
|
||||
protected abstract void UpdateState(ArmedState state);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Objects
|
||||
/// <summary>
|
||||
/// The time at which the HitObject starts.
|
||||
/// </summary>
|
||||
public double StartTime;
|
||||
public virtual double StartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The samples to be played when this hit object is hit.
|
||||
|
55
osu.Game/Rulesets/Objects/HitObjectStartTimeComparer.cs
Normal file
55
osu.Game/Rulesets/Objects/HitObjectStartTimeComparer.cs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares two hit objects by their start time, falling back to creation order if their start time is equal.
|
||||
/// </summary>
|
||||
public class HitObjectStartTimeComparer : Drawable.CreationOrderDepthComparer
|
||||
{
|
||||
public override int Compare(Drawable x, Drawable y)
|
||||
{
|
||||
var hitObjectX = x as DrawableHitObject;
|
||||
var hitObjectY = y as DrawableHitObject;
|
||||
|
||||
// If either of the two drawables are not hit objects, fall back to the base comparer
|
||||
if (hitObjectX?.HitObject == null || hitObjectY?.HitObject == null)
|
||||
return base.Compare(x, y);
|
||||
|
||||
// Compare by start time
|
||||
int i = hitObjectX.HitObject.StartTime.CompareTo(hitObjectY.HitObject.StartTime);
|
||||
if (i != 0)
|
||||
return i;
|
||||
|
||||
return base.Compare(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two hit objects by their start time, falling back to creation order if their start time is equal.
|
||||
/// This will compare the two hit objects in reverse order.
|
||||
/// </summary>
|
||||
public class HitObjectReverseStartTimeComparer : Drawable.ReverseCreationOrderDepthComparer
|
||||
{
|
||||
public override int Compare(Drawable x, Drawable y)
|
||||
{
|
||||
var hitObjectX = x as DrawableHitObject;
|
||||
var hitObjectY = y as DrawableHitObject;
|
||||
|
||||
// If either of the two drawables are not hit objects, fall back to the base comparer
|
||||
if (hitObjectX?.HitObject == null || hitObjectY?.HitObject == null)
|
||||
return base.Compare(x, y);
|
||||
|
||||
// Compare by start time
|
||||
int i = hitObjectY.HitObject.StartTime.CompareTo(hitObjectX.HitObject.StartTime);
|
||||
if (i != 0)
|
||||
return i;
|
||||
|
||||
return base.Compare(x, y);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,20 +6,30 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Database;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Legacy
|
||||
{
|
||||
internal abstract class ConvertSlider : HitObject, IHasCurve
|
||||
{
|
||||
/// <summary>
|
||||
/// Scoring distance with a speed-adjusted beat length of 1 second.
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public List<Vector2> ControlPoints { get; set; }
|
||||
public CurveType CurveType { get; set; }
|
||||
|
||||
public double Distance { get; set; }
|
||||
|
||||
public List<SampleInfoList> RepeatSamples { get; set; }
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
public double EndTime { get; set; }
|
||||
public double Duration { get; set; }
|
||||
public double EndTime => StartTime + RepeatCount * Distance / Velocity;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public double Velocity = 1;
|
||||
|
||||
public Vector2 PositionAt(double progress)
|
||||
{
|
||||
@ -35,5 +45,17 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
||||
|
||||
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / difficultyPoint.SpeedMultiplier;
|
||||
|
||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ namespace osu.Game.Screens.Menu
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Space:
|
||||
osuLogo.TriggerClick(state);
|
||||
osuLogo.TriggerOnClick(state);
|
||||
return true;
|
||||
case Key.Escape:
|
||||
switch (State)
|
||||
@ -144,7 +144,7 @@ namespace osu.Game.Screens.Menu
|
||||
State = MenuState.Initial;
|
||||
return true;
|
||||
case MenuState.Play:
|
||||
backButton.TriggerClick();
|
||||
backButton.TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -178,10 +178,10 @@ namespace osu.Game.Screens.Menu
|
||||
State = MenuState.TopLevel;
|
||||
return;
|
||||
case MenuState.TopLevel:
|
||||
buttonsTopLevel.First().TriggerClick();
|
||||
buttonsTopLevel.First().TriggerOnClick();
|
||||
return;
|
||||
case MenuState.Play:
|
||||
buttonsPlay.First().TriggerClick();
|
||||
buttonsPlay.First().TriggerOnClick();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
public MenuSideFlashes()
|
||||
{
|
||||
EarlyActivationMilliseconds = box_fade_in_time;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
259
osu.Game/Screens/Multiplayer/DrawableRoom.cs
Normal file
259
osu.Game/Screens/Multiplayer/DrawableRoom.cs
Normal file
@ -0,0 +1,259 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Screens.Multiplayer
|
||||
{
|
||||
public class DrawableRoom : ClickableContainer
|
||||
{
|
||||
private const float content_padding = 5;
|
||||
private const float height = 90;
|
||||
|
||||
private readonly Box sideStrip;
|
||||
private readonly UpdateableAvatar avatar;
|
||||
private readonly OsuSpriteText name;
|
||||
private readonly Container flagContainer;
|
||||
private readonly OsuSpriteText host;
|
||||
private readonly OsuSpriteText rankBounds;
|
||||
private readonly OsuSpriteText status;
|
||||
private readonly FillFlowContainer<OsuSpriteText> beatmapInfoFlow;
|
||||
private readonly OsuSpriteText beatmapTitle;
|
||||
private readonly OsuSpriteText beatmapDash;
|
||||
private readonly OsuSpriteText beatmapArtist;
|
||||
|
||||
private OsuColour colours;
|
||||
private LocalisationEngine localisation;
|
||||
|
||||
public readonly Room Room;
|
||||
|
||||
public DrawableRoom(Room room)
|
||||
{
|
||||
Room = room;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
CornerRadius = 5;
|
||||
Masking = true;
|
||||
EdgeEffect = new EdgeEffect
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Black.Opacity(40),
|
||||
Radius = 5,
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(34),
|
||||
},
|
||||
sideStrip = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = content_padding,
|
||||
},
|
||||
avatar = new UpdateableAvatar
|
||||
{
|
||||
Size = new Vector2(Height - content_padding* 2),
|
||||
Masking = true,
|
||||
CornerRadius = 5f,
|
||||
Margin = new MarginPadding { Left = content_padding * 2, Top = content_padding },
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Top = content_padding, Bottom = content_padding, Left = Height + content_padding * 2, Right = content_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(5f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
name = new OsuSpriteText
|
||||
{
|
||||
TextSize = 18,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 20f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5f, 0f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
flagContainer = new Container
|
||||
{
|
||||
Width = 30f,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Width = 40f,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "hosted by",
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
TextSize = 14,
|
||||
},
|
||||
host = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
},
|
||||
},
|
||||
},
|
||||
rankBounds = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Text = "#0 - #0",
|
||||
TextSize = 14,
|
||||
Margin = new MarginPadding { Right = 10 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Margin = new MarginPadding { Bottom = content_padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
status = new OsuSpriteText
|
||||
{
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-Bold",
|
||||
},
|
||||
beatmapInfoFlow = new FillFlowContainer<OsuSpriteText>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new[]
|
||||
{
|
||||
beatmapTitle = new OsuSpriteText
|
||||
{
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-BoldItalic",
|
||||
},
|
||||
beatmapDash = new OsuSpriteText
|
||||
{
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-RegularItalic",
|
||||
},
|
||||
beatmapArtist = new OsuSpriteText
|
||||
{
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-RegularItalic",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Room.Name.ValueChanged += displayName;
|
||||
Room.Host.ValueChanged += displayUser;
|
||||
Room.Status.ValueChanged += displayStatus;
|
||||
Room.Beatmap.ValueChanged += displayBeatmap;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, LocalisationEngine localisation)
|
||||
{
|
||||
this.localisation = localisation;
|
||||
this.colours = colours;
|
||||
|
||||
beatmapInfoFlow.Colour = rankBounds.Colour = colours.Gray9;
|
||||
host.Colour = colours.Blue;
|
||||
|
||||
displayStatus(Room.Status.Value);
|
||||
}
|
||||
|
||||
private void displayName(string value)
|
||||
{
|
||||
name.Text = value;
|
||||
}
|
||||
|
||||
private void displayUser(User value)
|
||||
{
|
||||
avatar.User = value;
|
||||
host.Text = value.Username;
|
||||
flagContainer.Children = new[] { new DrawableFlag(value.Country?.FlagName ?? @"__") { RelativeSizeAxes = Axes.Both } };
|
||||
}
|
||||
|
||||
private void displayStatus(RoomStatus value)
|
||||
{
|
||||
if (value == null) return;
|
||||
status.Text = value.Message;
|
||||
|
||||
foreach (Drawable d in new Drawable[] { sideStrip, status })
|
||||
d.FadeColour(value.GetAppropriateColour(colours), 100);
|
||||
}
|
||||
|
||||
private void displayBeatmap(BeatmapMetadata value)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
beatmapTitle.Current = localisation.GetUnicodePreference(value.TitleUnicode, value.Title);
|
||||
beatmapDash.Text = @" - ";
|
||||
beatmapArtist.Current = localisation.GetUnicodePreference(value.ArtistUnicode, value.Artist);
|
||||
}
|
||||
else
|
||||
{
|
||||
beatmapTitle.Current = null;
|
||||
beatmapArtist.Current = null;
|
||||
|
||||
beatmapTitle.Text = @"Changing map";
|
||||
beatmapDash.Text = string.Empty;
|
||||
beatmapArtist.Text = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
Room.Name.ValueChanged -= displayName;
|
||||
Room.Host.ValueChanged -= displayUser;
|
||||
Room.Status.ValueChanged -= displayStatus;
|
||||
Room.Beatmap.ValueChanged -= displayBeatmap;
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
if (!args.Repeat && args.Key == Key.Escape)
|
||||
{
|
||||
Buttons.Children.Last().TriggerClick();
|
||||
Buttons.Children.Last().TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -131,13 +131,13 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public override bool HandleInput => true;
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => target.Children.Any(c => c.TriggerKeyDown(state, args));
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => target.Children.Any(c => c.TriggerOnKeyDown(state, args));
|
||||
|
||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => target.Children.Any(c => c.TriggerKeyUp(state, args));
|
||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => target.Children.Any(c => c.TriggerOnKeyUp(state, args));
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => target.Children.Any(c => c.TriggerMouseDown(state, args));
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => target.Children.Any(c => c.TriggerOnMouseDown(state, args));
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => target.Children.Any(c => c.TriggerMouseUp(state, args));
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => target.Children.Any(c => c.TriggerOnMouseUp(state, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
if (!args.Repeat && args.Key == Key.Escape)
|
||||
{
|
||||
Buttons.Children.First().TriggerClick();
|
||||
Buttons.Children.First().TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
if (HasFailed || !ValidForResume || pauseContainer.AllowExit || HitRenderer.HasReplayLoaded)
|
||||
if (HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || HitRenderer?.HasReplayLoaded != false)
|
||||
{
|
||||
fadeOut();
|
||||
return base.OnExiting(next);
|
||||
@ -310,7 +310,7 @@ namespace osu.Game.Screens.Play
|
||||
HitRenderer?.FadeOut(fade_out_duration);
|
||||
Content.FadeOut(fade_out_duration);
|
||||
|
||||
hudOverlay.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In);
|
||||
hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In);
|
||||
|
||||
Background?.FadeTo(1f, fade_out_duration);
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ namespace osu.Game.Screens.Play
|
||||
switch (args.Key)
|
||||
{
|
||||
case Key.Space:
|
||||
button.TriggerClick();
|
||||
button.TriggerOnClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,8 @@ namespace osu.Game.Screens.Select
|
||||
public void Deactivate()
|
||||
{
|
||||
searchTextBox.HoldFocus = false;
|
||||
searchTextBox.TriggerFocusLost();
|
||||
if (searchTextBox.HasFocus)
|
||||
inputManager.ChangeFocus(searchTextBox);
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
@ -164,9 +165,13 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls:true)]
|
||||
private void load(OsuColour colours, OsuGame osu)
|
||||
private InputManager inputManager;
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuColour colours, OsuGame osu, UserInputManager inputManager)
|
||||
{
|
||||
this.inputManager = inputManager;
|
||||
|
||||
sortTabs.AccentColour = colours.GreenLight;
|
||||
|
||||
if (osu != null)
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Configuration;
|
||||
|
||||
namespace osu.Game.Users
|
||||
{
|
||||
@ -19,6 +20,8 @@ namespace osu.Game.Users
|
||||
[JsonProperty(@"country")]
|
||||
public Country Country;
|
||||
|
||||
public Bindable<UserStatus> Status = new Bindable<UserStatus>();
|
||||
|
||||
//public Team Team;
|
||||
|
||||
[JsonProperty(@"profile_colour")]
|
||||
|
@ -158,14 +158,19 @@ namespace osu.Game.Users
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Status.ValueChanged += displayStatus;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
this.colours = colours;
|
||||
Status.ValueChanged += displayStatus;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Status.TriggerChange();
|
||||
}
|
||||
|
||||
private void displayStatus(UserStatus status)
|
||||
|
@ -58,4 +58,10 @@ namespace osu.Game.Users
|
||||
public override string Message => @"Modding a map";
|
||||
public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark;
|
||||
}
|
||||
|
||||
public class UserStatusDoNotDisturb : UserStatus
|
||||
{
|
||||
public override string Message => @"Do not disturb";
|
||||
public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark;
|
||||
}
|
||||
}
|
||||
|
@ -176,6 +176,7 @@
|
||||
<Compile Include="Rulesets\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
|
||||
<Compile Include="Rulesets\Judgements\Judgement.cs" />
|
||||
<Compile Include="Rulesets\Objects\HitObjectParser.cs" />
|
||||
<Compile Include="Rulesets\Objects\HitObjectStartTimeComparer.cs" />
|
||||
<Compile Include="Rulesets\Objects\Types\IHasCombo.cs" />
|
||||
<Compile Include="Rulesets\Objects\Types\IHasEndTime.cs" />
|
||||
<Compile Include="Rulesets\Objects\Types\IHasDistance.cs" />
|
||||
@ -432,6 +433,9 @@
|
||||
<Compile Include="Overlays\Music\PlaylistOverlay.cs" />
|
||||
<Compile Include="Rulesets\Replays\IAutoGenerator.cs" />
|
||||
<Compile Include="Rulesets\Replays\AutoGenerator.cs" />
|
||||
<Compile Include="Screens\Multiplayer\DrawableRoom.cs" />
|
||||
<Compile Include="Online\Multiplayer\Room.cs" />
|
||||
<Compile Include="Online\Multiplayer\RoomStatus.cs" />
|
||||
<Compile Include="Users\UserPanel.cs" />
|
||||
<Compile Include="Users\UserStatus.cs" />
|
||||
<Compile Include="Overlays\DirectOverlay.cs" />
|
||||
@ -447,6 +451,7 @@
|
||||
<Compile Include="Overlays\Direct\SlimEnumDropdown.cs" />
|
||||
<Compile Include="Graphics\Containers\ReverseDepthFillFlowContainer.cs" />
|
||||
<Compile Include="Database\RankStatus.cs" />
|
||||
<Compile Include="Graphics\UserInterface\BreadcrumbControl.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
|
||||
|
Loading…
Reference in New Issue
Block a user