1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 09:02:58 +08:00

Merge branch 'master' into taiko_drawable_drawing

Conflicts:
	osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
	osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs
	osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj
This commit is contained in:
smoogipooo 2017-03-24 15:11:56 +09:00
commit 20e80a758a
183 changed files with 2331 additions and 672 deletions

@ -1 +1 @@
Subproject commit 42ec8a62fb697f1d967a927549dc51336cd66968 Subproject commit 51737ec1320b4ea9dce4978f24e1d77a28f0a98e

View File

@ -53,7 +53,7 @@ namespace osu.Desktop.Deploy
private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg"; private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg";
private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg"; private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg";
private static Stopwatch sw = new Stopwatch(); private static readonly Stopwatch sw = new Stopwatch();
private static string codeSigningPassword; private static string codeSigningPassword;

View File

@ -15,7 +15,7 @@ namespace osu.Desktop.VisualTests.Beatmaps
this.beatmap = beatmap; this.beatmap = beatmap;
} }
private Beatmap beatmap; private readonly Beatmap beatmap;
protected override Beatmap GetBeatmap() => beatmap; protected override Beatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null; protected override Texture GetBackground() => null;

View File

@ -10,7 +10,7 @@ namespace osu.Desktop.VisualTests
{ {
public class Benchmark : OsuGameBase public class Benchmark : OsuGameBase
{ {
private double timePerTest = 200; private const double time_per_test = 200;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
@ -27,7 +27,7 @@ namespace osu.Desktop.VisualTests
TestBrowser f = new TestBrowser(); TestBrowser f = new TestBrowser();
Add(f); Add(f);
Console.WriteLine($@"{Time}: Running {f.TestCount} tests for {timePerTest}ms each..."); Console.WriteLine($@"{Time}: Running {f.TestCount} tests for {time_per_test}ms each...");
for (int i = 1; i < f.TestCount; i++) for (int i = 1; i < f.TestCount; i++)
{ {
@ -36,10 +36,10 @@ namespace osu.Desktop.VisualTests
{ {
f.LoadTest(loadableCase); f.LoadTest(loadableCase);
Console.WriteLine($@"{Time}: Switching to test #{loadableCase}"); Console.WriteLine($@"{Time}: Switching to test #{loadableCase}");
}, loadableCase * timePerTest); }, loadableCase * time_per_test);
} }
Scheduler.AddDelayed(Host.Exit, f.TestCount * timePerTest); Scheduler.AddDelayed(Host.Exit, f.TestCount * time_per_test);
} }
} }
} }

View File

@ -0,0 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Screens.Testing;
using osu.Game.Screens.Select;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseBeatmapDetailArea : TestCase
{
public override string Description => @"Beatmap details in song select";
public override void Reset()
{
base.Reset();
Add(new BeatmapDetailArea
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(550f, 450f),
});
}
}
}

View File

@ -21,7 +21,7 @@ namespace osu.Desktop.VisualTests.Tests
{ {
internal class TestCaseHitObjects : TestCase internal class TestCaseHitObjects : TestCase
{ {
private FramedClock framedClock; private readonly FramedClock framedClock;
private bool auto; private bool auto;
@ -34,7 +34,7 @@ namespace osu.Desktop.VisualTests.Tests
private HitObjectType mode = HitObjectType.Slider; private HitObjectType mode = HitObjectType.Slider;
private BindableNumber<double> playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 }; private readonly BindableNumber<double> playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 };
private Container playfieldContainer; private Container playfieldContainer;
private Container approachContainer; private Container approachContainer;
@ -136,7 +136,7 @@ namespace osu.Desktop.VisualTests.Tests
if (auto) if (auto)
{ {
h.State = ArmedState.Hit; h.State = ArmedState.Hit;
h.Judgement = new OsuJudgementInfo { Result = HitResult.Hit }; h.Judgement = new OsuJudgement { Result = HitResult.Hit };
} }
playfieldContainer.Add(h); playfieldContainer.Add(h);

View File

@ -4,9 +4,9 @@
using OpenTK; using OpenTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Game.Modes;
using osu.Game.Modes.Mods; using osu.Game.Modes.Mods;
using osu.Game.Modes.Osu.Mods; using osu.Game.Modes.Osu.Mods;
using osu.Game.Modes.Scoring;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;

View File

@ -95,7 +95,7 @@ namespace osu.Desktop.VisualTests.Tests
progressingNotifications.Add(n); progressingNotifications.Add(n);
} }
private List<ProgressNotification> progressingNotifications = new List<ProgressNotification>(); private readonly List<ProgressNotification> progressingNotifications = new List<ProgressNotification>();
private void sendProgress1() private void sendProgress1()
{ {

View File

@ -0,0 +1,77 @@
// 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.MathUtils;
using osu.Framework.Screens.Testing;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.Taiko.UI;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseTaikoPlayfield : TestCase
{
public override string Description => "Taiko playfield";
private TaikoPlayfield playfield;
public override void Reset()
{
base.Reset();
AddButton("Hit!", addHitJudgement);
AddButton("Miss :(", addMissJudgement);
Add(playfield = new TaikoPlayfield
{
Y = 200
});
}
private void addHitJudgement()
{
TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great;
playfield.OnJudgement(new DrawableTestHit(new Hit())
{
X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f),
Judgement = new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = hitResult,
TimeOffset = 0,
ComboAtHit = 1,
SecondHit = RNG.Next(10) == 0
}
});
}
private void addMissJudgement()
{
playfield.OnJudgement(new DrawableTestHit(new Hit())
{
Judgement = new TaikoJudgement
{
Result = HitResult.Miss,
TimeOffset = 0,
ComboAtHit = 0
}
});
}
private class DrawableTestHit : DrawableHitObject<TaikoHitObject, TaikoJudgement>
{
public DrawableTestHit(TaikoHitObject hitObject)
: base(hitObject)
{
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{
}
}
}
}

View File

@ -195,6 +195,7 @@
<Compile Include="Tests\TestCaseScoreCounter.cs" /> <Compile Include="Tests\TestCaseScoreCounter.cs" />
<Compile Include="Tests\TestCaseTabControl.cs" /> <Compile Include="Tests\TestCaseTabControl.cs" />
<Compile Include="Tests\TestCaseTaikoHitObjects.cs" /> <Compile Include="Tests\TestCaseTaikoHitObjects.cs" />
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
<Compile Include="Tests\TestCaseTextAwesome.cs" /> <Compile Include="Tests\TestCaseTextAwesome.cs" />
<Compile Include="Tests\TestCasePlaySongSelect.cs" /> <Compile Include="Tests\TestCasePlaySongSelect.cs" />
<Compile Include="Tests\TestCaseTwoLayerButton.cs" /> <Compile Include="Tests\TestCaseTwoLayerButton.cs" />
@ -207,6 +208,7 @@
<Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" /> <Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" />
<Compile Include="Tests\TestCaseLeaderboard.cs" /> <Compile Include="Tests\TestCaseLeaderboard.cs" />
<Compile Include="Beatmaps\TestWorkingBeatmap.cs" /> <Compile Include="Beatmaps\TestWorkingBeatmap.cs" />
<Compile Include="Tests\TestCaseBeatmapDetailArea.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup /> <ItemGroup />

View File

@ -17,7 +17,7 @@ namespace osu.Desktop
{ {
internal class OsuGameDesktop : OsuGame internal class OsuGameDesktop : OsuGame
{ {
private VersionManager versionManager; private readonly VersionManager versionManager;
public OsuGameDesktop(string[] args = null) public OsuGameDesktop(string[] args = null)
: base(args) : base(args)

View File

@ -10,6 +10,8 @@ using osu.Game.Modes.Mods;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Modes.Catch.Scoring;
using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Catch namespace osu.Game.Modes.Catch
{ {

View File

@ -5,7 +5,10 @@ using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Catch.Judgements namespace osu.Game.Modes.Catch.Judgements
{ {
public class CatchJudgementInfo : JudgementInfo public class CatchJudgement : Judgement
{ {
public override string ResultString => string.Empty;
public override string MaxResultString => string.Empty;
} }
} }

View File

@ -12,7 +12,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{ {
internal class DrawableFruit : Sprite internal class DrawableFruit : Sprite
{ {
private CatchBaseHit h; private readonly CatchBaseHit h;
public DrawableFruit(CatchBaseHit h) public DrawableFruit(CatchBaseHit h)
{ {
@ -29,7 +29,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); Texture = textures.Get(@"Menu/logo");
double duration = 0; const double duration = 0;
Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) }); Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) });
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 }); Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 });

View File

@ -3,22 +3,23 @@
using osu.Game.Modes.Catch.Judgements; using osu.Game.Modes.Catch.Judgements;
using osu.Game.Modes.Catch.Objects; using osu.Game.Modes.Catch.Objects;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Catch namespace osu.Game.Modes.Catch.Scoring
{ {
internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit, CatchJudgementInfo> internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit, CatchJudgement>
{ {
public CatchScoreProcessor() public CatchScoreProcessor()
{ {
} }
public CatchScoreProcessor(HitRenderer<CatchBaseHit, CatchJudgementInfo> hitRenderer) public CatchScoreProcessor(HitRenderer<CatchBaseHit, CatchJudgement> hitRenderer)
: base(hitRenderer) : base(hitRenderer)
{ {
} }
protected override void UpdateCalculations(CatchJudgementInfo newJudgement) protected override void OnNewJugement(CatchJudgement judgement)
{ {
} }
} }

View File

@ -5,12 +5,14 @@ using osu.Game.Beatmaps;
using osu.Game.Modes.Catch.Beatmaps; using osu.Game.Modes.Catch.Beatmaps;
using osu.Game.Modes.Catch.Judgements; using osu.Game.Modes.Catch.Judgements;
using osu.Game.Modes.Catch.Objects; using osu.Game.Modes.Catch.Objects;
using osu.Game.Modes.Catch.Scoring;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Catch.UI namespace osu.Game.Modes.Catch.UI
{ {
public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgementInfo> public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgement>
{ {
public CatchHitRenderer(WorkingBeatmap beatmap) public CatchHitRenderer(WorkingBeatmap beatmap)
: base(beatmap) : base(beatmap)
@ -23,8 +25,8 @@ namespace osu.Game.Modes.Catch.UI
protected override IBeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor(); protected override IBeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
protected override Playfield<CatchBaseHit, CatchJudgementInfo> CreatePlayfield() => new CatchPlayfield(); protected override Playfield<CatchBaseHit, CatchJudgement> CreatePlayfield() => new CatchPlayfield();
protected override DrawableHitObject<CatchBaseHit, CatchJudgementInfo> GetVisualRepresentation(CatchBaseHit h) => null; protected override DrawableHitObject<CatchBaseHit, CatchJudgement> GetVisualRepresentation(CatchBaseHit h) => null;
} }
} }

View File

@ -10,7 +10,7 @@ using osu.Game.Modes.Catch.Judgements;
namespace osu.Game.Modes.Catch.UI namespace osu.Game.Modes.Catch.UI
{ {
public class CatchPlayfield : Playfield<CatchBaseHit, CatchJudgementInfo> public class CatchPlayfield : Playfield<CatchBaseHit, CatchJudgement>
{ {
public CatchPlayfield() public CatchPlayfield()
{ {

View File

@ -50,8 +50,8 @@
<Compile Include="Beatmaps\CatchBeatmapConverter.cs" /> <Compile Include="Beatmaps\CatchBeatmapConverter.cs" />
<Compile Include="Beatmaps\CatchBeatmapProcessor.cs" /> <Compile Include="Beatmaps\CatchBeatmapProcessor.cs" />
<Compile Include="CatchDifficultyCalculator.cs" /> <Compile Include="CatchDifficultyCalculator.cs" />
<Compile Include="CatchScoreProcessor.cs" /> <Compile Include="Scoring\CatchScoreProcessor.cs" />
<Compile Include="Judgements\CatchJudgementInfo.cs" /> <Compile Include="Judgements\CatchJudgement.cs" />
<Compile Include="Objects\CatchBaseHit.cs" /> <Compile Include="Objects\CatchBaseHit.cs" />
<Compile Include="Objects\Drawable\DrawableFruit.cs" /> <Compile Include="Objects\Drawable\DrawableFruit.cs" />
<Compile Include="Objects\Droplet.cs" /> <Compile Include="Objects\Droplet.cs" />

View File

@ -5,7 +5,10 @@ using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Mania.Judgements namespace osu.Game.Modes.Mania.Judgements
{ {
public class ManiaJudgementInfo : JudgementInfo public class ManiaJudgement : Judgement
{ {
public override string ResultString => string.Empty;
public override string MaxResultString => string.Empty;
} }
} }

View File

@ -9,6 +9,8 @@ using osu.Game.Modes.Mods;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Modes.Mania.Scoring;
using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Mania namespace osu.Game.Modes.Mania
{ {

View File

@ -26,7 +26,7 @@ namespace osu.Game.Modes.Mania.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); Texture = textures.Get(@"Menu/logo");
double duration = 0; const double duration = 0;
Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f }); Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f });
Transforms.Add(new TransformAlpha { StartTime = note.StartTime + duration + 200, EndTime = note.StartTime + duration + 400, StartValue = 1, EndValue = 0 }); Transforms.Add(new TransformAlpha { StartTime = note.StartTime + duration + 200, EndTime = note.StartTime + duration + 400, StartValue = 1, EndValue = 0 });

View File

@ -3,22 +3,23 @@
using osu.Game.Modes.Mania.Judgements; using osu.Game.Modes.Mania.Judgements;
using osu.Game.Modes.Mania.Objects; using osu.Game.Modes.Mania.Objects;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Mania namespace osu.Game.Modes.Mania.Scoring
{ {
internal class ManiaScoreProcessor : ScoreProcessor<ManiaBaseHit, ManiaJudgementInfo> internal class ManiaScoreProcessor : ScoreProcessor<ManiaBaseHit, ManiaJudgement>
{ {
public ManiaScoreProcessor() public ManiaScoreProcessor()
{ {
} }
public ManiaScoreProcessor(HitRenderer<ManiaBaseHit, ManiaJudgementInfo> hitRenderer) public ManiaScoreProcessor(HitRenderer<ManiaBaseHit, ManiaJudgement> hitRenderer)
: base(hitRenderer) : base(hitRenderer)
{ {
} }
protected override void UpdateCalculations(ManiaJudgementInfo newJudgement) protected override void OnNewJugement(ManiaJudgement judgement)
{ {
} }
} }

View File

@ -5,12 +5,14 @@ using osu.Game.Beatmaps;
using osu.Game.Modes.Mania.Beatmaps; using osu.Game.Modes.Mania.Beatmaps;
using osu.Game.Modes.Mania.Judgements; using osu.Game.Modes.Mania.Judgements;
using osu.Game.Modes.Mania.Objects; using osu.Game.Modes.Mania.Objects;
using osu.Game.Modes.Mania.Scoring;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Mania.UI namespace osu.Game.Modes.Mania.UI
{ {
public class ManiaHitRenderer : HitRenderer<ManiaBaseHit, ManiaJudgementInfo> public class ManiaHitRenderer : HitRenderer<ManiaBaseHit, ManiaJudgement>
{ {
private readonly int columns; private readonly int columns;
@ -26,8 +28,8 @@ namespace osu.Game.Modes.Mania.UI
protected override IBeatmapProcessor<ManiaBaseHit> CreateBeatmapProcessor() => new ManiaBeatmapProcessor(); protected override IBeatmapProcessor<ManiaBaseHit> CreateBeatmapProcessor() => new ManiaBeatmapProcessor();
protected override Playfield<ManiaBaseHit, ManiaJudgementInfo> CreatePlayfield() => new ManiaPlayfield(columns); protected override Playfield<ManiaBaseHit, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(columns);
protected override DrawableHitObject<ManiaBaseHit, ManiaJudgementInfo> GetVisualRepresentation(ManiaBaseHit h) => null; protected override DrawableHitObject<ManiaBaseHit, ManiaJudgement> GetVisualRepresentation(ManiaBaseHit h) => null;
} }
} }

View File

@ -11,7 +11,7 @@ using osu.Game.Modes.Mania.Judgements;
namespace osu.Game.Modes.Mania.UI namespace osu.Game.Modes.Mania.UI
{ {
public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgementInfo> public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgement>
{ {
public ManiaPlayfield(int columns) public ManiaPlayfield(int columns)
{ {

View File

@ -49,9 +49,9 @@
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" /> <Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
<Compile Include="Beatmaps\ManiaBeatmapProcessor.cs" /> <Compile Include="Beatmaps\ManiaBeatmapProcessor.cs" />
<Compile Include="Judgements\ManiaJudgementInfo.cs" /> <Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="ManiaScoreProcessor.cs" /> <Compile Include="Scoring\ManiaScoreProcessor.cs" />
<Compile Include="Objects\Drawable\DrawableNote.cs" /> <Compile Include="Objects\Drawable\DrawableNote.cs" />
<Compile Include="Objects\HoldNote.cs" /> <Compile Include="Objects\HoldNote.cs" />
<Compile Include="Objects\ManiaBaseHit.cs" /> <Compile Include="Objects\ManiaBaseHit.cs" />

View File

@ -44,11 +44,8 @@ namespace osu.Game.Modes.Osu.Beatmaps
{ {
StartTime = original.StartTime, StartTime = original.StartTime,
Sample = original.Sample, Sample = original.Sample,
CurveObject = curveData, CurveObject = curveData,
Position = positionData?.Position ?? Vector2.Zero, Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false NewCombo = comboData?.NewCombo ?? false
}; };
} }
@ -60,7 +57,6 @@ namespace osu.Game.Modes.Osu.Beatmaps
StartTime = original.StartTime, StartTime = original.StartTime,
Sample = original.Sample, Sample = original.Sample,
Position = new Vector2(512, 384) / 2, Position = new Vector2(512, 384) / 2,
EndTime = endTimeData.EndTime EndTime = endTimeData.EndTime
}; };
} }
@ -69,9 +65,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
{ {
StartTime = original.StartTime, StartTime = original.StartTime,
Sample = original.Sample, Sample = original.Sample,
Position = positionData?.Position ?? Vector2.Zero, Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false NewCombo = comboData?.NewCombo ?? false
}; };
} }
@ -81,7 +75,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
if (endIndex == -1) if (endIndex == -1)
endIndex = hitObjects.Count - 1; endIndex = hitObjects.Count - 1;
int stackDistance = 3; const int stack_distance = 3;
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * stackLeniency; float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * stackLeniency;
// Reset stacking inside the update range // Reset stacking inside the update range
@ -108,8 +102,8 @@ namespace osu.Game.Modes.Osu.Beatmaps
//We are no longer within stacking range of the next object. //We are no longer within stacking range of the next object.
break; break;
if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stackDistance || if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stackDistance) stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{ {
stackBaseIndex = n; stackBaseIndex = n;
@ -174,14 +168,14 @@ namespace osu.Game.Modes.Osu.Beatmaps
* o <- hitCircle has stack of -1 * o <- hitCircle has stack of -1
* o <- hitCircle has stack of -2 * o <- hitCircle has stack of -2
*/ */
if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stackDistance) if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{ {
int offset = objectI.StackHeight - objectN.StackHeight + 1; int offset = objectI.StackHeight - objectN.StackHeight + 1;
for (int j = n + 1; j <= i; j++) for (int j = n + 1; j <= i; j++)
{ {
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).
OsuHitObject objectJ = hitObjects[j]; OsuHitObject objectJ = hitObjects[j];
if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stackDistance) if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
objectJ.StackHeight -= offset; objectJ.StackHeight -= offset;
} }
@ -190,7 +184,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
break; break;
} }
if (Vector2.Distance(objectN.Position, objectI.Position) < stackDistance) if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance)
{ {
//Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. //Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out.
//NOTE: Sliders with start positions stacking are a special case that is also handled here. //NOTE: Sliders with start positions stacking are a special case that is also handled here.
@ -214,7 +208,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
//We are no longer within stacking range of the previous object. //We are no longer within stacking range of the previous object.
break; break;
if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stackDistance) if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{ {
objectN.StackHeight = objectI.StackHeight + 1; objectN.StackHeight = objectI.StackHeight + 1;
objectI = objectN; objectI = objectN;

View File

@ -4,10 +4,11 @@
using OpenTK; using OpenTK;
using osu.Game.Modes.Judgements; using osu.Game.Modes.Judgements;
using osu.Game.Modes.Osu.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Framework.Extensions;
namespace osu.Game.Modes.Osu.Judgements namespace osu.Game.Modes.Osu.Judgements
{ {
public class OsuJudgementInfo : JudgementInfo public class OsuJudgement : Judgement
{ {
/// <summary> /// <summary>
/// The positional hit offset. /// The positional hit offset.
@ -24,6 +25,10 @@ namespace osu.Game.Modes.Osu.Judgements
/// </summary> /// </summary>
public OsuScoreResult MaxScore = OsuScoreResult.Hit300; public OsuScoreResult MaxScore = OsuScoreResult.Hit300;
public override string ResultString => Score.GetDescription();
public override string MaxResultString => MaxScore.GetDescription();
public int ScoreValue => scoreToInt(Score); public int ScoreValue => scoreToInt(Score);
public int MaxScoreValue => scoreToInt(MaxScore); public int MaxScoreValue => scoreToInt(MaxScore);

View File

@ -7,6 +7,7 @@ using osu.Game.Modes.Mods;
using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects;
using System; using System;
using System.Linq; using System.Linq;
using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Osu.Mods namespace osu.Game.Modes.Osu.Mods
{ {

View File

@ -13,34 +13,30 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{ {
private OsuHitObject osuObject;
public ApproachCircle ApproachCircle; public ApproachCircle ApproachCircle;
private CirclePiece circle; private readonly CirclePiece circle;
private RingPiece ring; private readonly RingPiece ring;
private FlashPiece flash; private readonly FlashPiece flash;
private ExplodePiece explode; private readonly ExplodePiece explode;
private NumberPiece number; private readonly NumberPiece number;
private GlowPiece glow; private readonly GlowPiece glow;
public DrawableHitCircle(OsuHitObject h) : base(h) public DrawableHitCircle(OsuHitObject h) : base(h)
{ {
Origin = Anchor.Centre; Origin = Anchor.Centre;
osuObject = h; Position = HitObject.StackedPosition;
Scale = new Vector2(HitObject.Scale);
Position = osuObject.StackedPosition;
Scale = new Vector2(osuObject.Scale);
Children = new Drawable[] Children = new Drawable[]
{ {
glow = new GlowPiece glow = new GlowPiece
{ {
Colour = osuObject.ComboColour Colour = AccentColour
}, },
circle = new CirclePiece circle = new CirclePiece
{ {
Colour = osuObject.ComboColour, Colour = AccentColour,
Hit = () => Hit = () =>
{ {
if (Judgement.Result.HasValue) return false; if (Judgement.Result.HasValue) return false;
@ -58,11 +54,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
flash = new FlashPiece(), flash = new FlashPiece(),
explode = new ExplodePiece explode = new ExplodePiece
{ {
Colour = osuObject.ComboColour, Colour = AccentColour,
}, },
ApproachCircle = new ApproachCircle ApproachCircle = new ApproachCircle
{ {
Colour = osuObject.ComboColour, Colour = AccentColour,
} }
}; };
@ -115,8 +111,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ApproachCircle.FadeOut(); ApproachCircle.FadeOut();
double endTime = (osuObject as IHasEndTime)?.EndTime ?? osuObject.StartTime; double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
double duration = endTime - osuObject.StartTime; double duration = endTime - HitObject.StartTime;
glow.Delay(duration); glow.Delay(duration);
glow.FadeOut(400); glow.FadeOut(400);

View File

@ -7,18 +7,19 @@ using osu.Game.Modes.Osu.Judgements;
namespace osu.Game.Modes.Osu.Objects.Drawables namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject, OsuJudgementInfo> public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject, OsuJudgement>
{ {
public const float TIME_PREEMPT = 600; public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400; public const float TIME_FADEIN = 400;
public const float TIME_FADEOUT = 500; public const float TIME_FADEOUT = 500;
public DrawableOsuHitObject(OsuHitObject hitObject) protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
AccentColour = HitObject.ComboColour;
} }
protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 }; protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 };
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {

View 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 osu.Framework.Graphics.Transforms;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
using OpenTK;
using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement<OsuJudgement>
{
public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement)
{
}
protected override void LoadComplete()
{
if (Judgement.Result != HitResult.Miss)
JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
base.LoadComplete();
}
}
}

View File

@ -13,18 +13,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{ {
private Slider slider; private readonly Slider slider;
private DrawableHitCircle initialCircle; private readonly DrawableHitCircle initialCircle;
private List<ISliderProgress> components = new List<ISliderProgress>(); private readonly List<ISliderProgress> components = new List<ISliderProgress>();
private Container<DrawableSliderTick> ticks; private readonly Container<DrawableSliderTick> ticks;
private SliderBody body; private readonly SliderBody body;
private SliderBall ball; private readonly SliderBall ball;
private SliderBouncer bouncer2; private readonly SliderBouncer bouncer2;
public DrawableSlider(Slider s) : base(s) public DrawableSlider(Slider s) : base(s)
{ {
@ -39,6 +39,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
body = new SliderBody(s) body = new SliderBody(s)
{ {
AccentColour = AccentColour,
Position = s.StackedPosition, Position = s.StackedPosition,
PathWidth = s.Scale * 64, PathWidth = s.Scale * 64,
}, },
@ -56,6 +57,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ball = new SliderBall(s) ball = new SliderBall(s)
{ {
Scale = new Vector2(s.Scale), Scale = new Vector2(s.Scale),
AccentColour = AccentColour
}, },
initialCircle = new DrawableHitCircle(new HitCircle initialCircle = new DrawableHitCircle(new HitCircle
{ {

View File

@ -18,7 +18,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
public class DrawableSliderTick : DrawableOsuHitObject public class DrawableSliderTick : DrawableOsuHitObject
{ {
private SliderTick sliderTick; private readonly SliderTick sliderTick;
public double FadeInTime; public double FadeInTime;
public double FadeOutTime; public double FadeOutTime;
@ -27,7 +27,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
public override bool RemoveWhenNotAlive => false; public override bool RemoveWhenNotAlive => false;
protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick }; protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.SliderTick };
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{ {
@ -48,7 +48,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = sliderTick.ComboColour, Colour = AccentColour,
Alpha = 0.3f, Alpha = 0.3f,
} }
}; };

View File

@ -15,12 +15,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
public class DrawableSpinner : DrawableOsuHitObject public class DrawableSpinner : DrawableOsuHitObject
{ {
private Spinner spinner; private readonly Spinner spinner;
private SpinnerDisc disc; private readonly SpinnerDisc disc;
private SpinnerBackground background; private readonly SpinnerBackground background;
private Container circleContainer; private readonly Container circleContainer;
private DrawableHitCircle circle; private readonly DrawableHitCircle circle;
public DrawableSpinner(Spinner s) : base(s) public DrawableSpinner(Spinner s) : base(s)
{ {
@ -48,7 +48,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
Alpha = 0, Alpha = 0,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
DiscColour = s.ComboColour DiscColour = AccentColour
}, },
circleContainer = new Container circleContainer = new Container
{ {
@ -108,9 +108,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f; private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f;
private float spinsPerMinuteNeeded = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5 private const float spins_per_minute_needed = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5
private float rotationsNeeded => (float)(spinsPerMinuteNeeded * (spinner.EndTime - spinner.StartTime) / 60000f); private float rotationsNeeded => (float)(spins_per_minute_needed * (spinner.EndTime - spinner.StartTime) / 60000f);
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / rotationsNeeded, 0, 1); public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / rotationsNeeded, 0, 1);

View File

@ -1,86 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.Sprites;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class HitExplosion : FillFlowContainer
{
private readonly OsuJudgementInfo judgement;
private SpriteText line1;
private SpriteText line2;
public HitExplosion(OsuJudgementInfo judgement, OsuHitObject h = null)
{
this.judgement = judgement;
AutoSizeAxes = Axes.Both;
Origin = Anchor.Centre;
Direction = FillDirection.Vertical;
Spacing = new Vector2(0, 2);
Position = (h?.StackedEndPosition ?? Vector2.Zero) + judgement.PositionOffset;
Children = new Drawable[]
{
line1 = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = judgement.Score.GetDescription(),
Font = @"Venera",
TextSize = 16,
},
line2 = new OsuSpriteText
{
Text = judgement.Combo.GetDescription(),
Font = @"Venera",
TextSize = 11,
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
if (judgement.Result == HitResult.Miss)
{
FadeInFromZero(60);
ScaleTo(1.6f);
ScaleTo(1, 100, EasingTypes.In);
MoveToOffset(new Vector2(0, 100), 800, EasingTypes.InQuint);
RotateTo(40, 800, EasingTypes.InQuint);
Delay(600);
FadeOut(200);
}
else
{
line1.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
line2.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
FadeOut(500);
}
switch (judgement.Result)
{
case HitResult.Miss:
Colour = Color4.Red;
break;
}
Expire();
}
}
}

View File

@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
public class ApproachCircle : Container public class ApproachCircle : Container
{ {
private Sprite approachCircle; private readonly Sprite approachCircle;
public ApproachCircle() public ApproachCircle()
{ {

View File

@ -14,7 +14,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
public class CirclePiece : Container public class CirclePiece : Container
{ {
private Sprite disc; private readonly Sprite disc;
public Func<bool> Hit; public Func<bool> Hit;

View File

@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
public class GlowPiece : Container public class GlowPiece : Container
{ {
private Sprite layer; private readonly Sprite layer;
public GlowPiece() public GlowPiece()
{ {

View File

@ -12,7 +12,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
public class NumberPiece : Container public class NumberPiece : Container
{ {
private SpriteText number; private readonly SpriteText number;
public string Text public string Text
{ {

View File

@ -12,11 +12,27 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
public class SliderBall : CircularContainer, ISliderProgress public class SliderBall : CircularContainer, ISliderProgress
{ {
private readonly Slider slider;
private Box follow;
private const float width = 128; private const float width = 128;
private Color4 accentColour = Color4.Black;
/// <summary>
/// The colour that is used for the slider ball.
/// </summary>
public Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
if (ball != null)
ball.Colour = value;
}
}
private readonly Slider slider;
private readonly Box follow;
private readonly Box ball;
public SliderBall(Slider slider) public SliderBall(Slider slider)
{ {
this.slider = slider; this.slider = slider;
@ -49,9 +65,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
Alpha = 1, Alpha = 1,
Children = new[] Children = new[]
{ {
new Box ball = new Box
{ {
Colour = slider.ComboColour, Colour = AccentColour,
Alpha = 0.4f, Alpha = 0.4f,
Width = width, Width = width,
Height = width, Height = width,

View File

@ -13,27 +13,45 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Configuration; using osu.Game.Configuration;
using OpenTK; using OpenTK;
using OpenTK.Graphics.ES30; using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
public class SliderBody : Container, ISliderProgress public class SliderBody : Container, ISliderProgress
{ {
private Path path; private readonly Path path;
private BufferedContainer container; private readonly BufferedContainer container;
public float PathWidth public float PathWidth
{ {
get { return path.PathWidth; } get { return path.PathWidth; }
set set { path.PathWidth = value; }
{
path.PathWidth = value;
}
} }
public double? SnakedStart { get; private set; } public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; } public double? SnakedEnd { get; private set; }
private Slider slider; private Color4 accentColour;
/// <summary>
/// Used to colour the path.
/// </summary>
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
if (LoadState == LoadState.Loaded)
Schedule(reloadTexture);
}
}
private int textureWidth => (int)PathWidth * 2;
private readonly Slider slider;
public SliderBody(Slider s) public SliderBody(Slider s)
{ {
slider = s; slider = s;
@ -82,7 +100,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
snakingIn = config.GetBindable<bool>(OsuConfig.SnakingInSliders); snakingIn = config.GetBindable<bool>(OsuConfig.SnakingInSliders);
snakingOut = config.GetBindable<bool>(OsuConfig.SnakingOutSliders); snakingOut = config.GetBindable<bool>(OsuConfig.SnakingOutSliders);
int textureWidth = (int)PathWidth * 2; reloadTexture();
}
private void reloadTexture()
{
var texture = new Texture(textureWidth, 1);
//initialise background //initialise background
var upload = new TextureUpload(textureWidth * 4); var upload = new TextureUpload(textureWidth * 4);
@ -110,19 +133,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
progress -= border_portion; progress -= border_portion;
bytes[i * 4] = (byte)(slider.ComboColour.R * 255); bytes[i * 4] = (byte)(AccentColour.R * 255);
bytes[i * 4 + 1] = (byte)(slider.ComboColour.G * 255); bytes[i * 4 + 1] = (byte)(AccentColour.G * 255);
bytes[i * 4 + 2] = (byte)(slider.ComboColour.B * 255); bytes[i * 4 + 2] = (byte)(AccentColour.B * 255);
bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (slider.ComboColour.A * 255)); bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255));
} }
} }
var texture = new Texture(textureWidth, 1);
texture.SetData(upload); texture.SetData(upload);
path.Texture = texture; path.Texture = texture;
} }
private List<Vector2> currentCurve = new List<Vector2>(); private readonly List<Vector2> currentCurve = new List<Vector2>();
private bool updateSnaking(double p0, double p1) private bool updateSnaking(double p0, double p1)
{ {
if (SnakedStart == p0 && SnakedEnd == p1) return false; if (SnakedStart == p0 && SnakedEnd == p1) return false;

View File

@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
private readonly Slider slider; private readonly Slider slider;
private readonly bool isEnd; private readonly bool isEnd;
private TextAwesome icon; private readonly TextAwesome icon;
public SliderBouncer(Slider slider, bool isEnd) public SliderBouncer(Slider slider, bool isEnd)
{ {

View File

@ -36,7 +36,7 @@ namespace osu.Game.Modes.Osu.Objects
public float Scale { get; set; } = 1; public float Scale { get; set; } = 1;
public Color4 ComboColour { get; set; } public Color4 ComboColour { get; set; } = Color4.Gray;
public virtual bool NewCombo { get; set; } public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; } public int ComboIndex { get; set; }

View File

@ -47,11 +47,11 @@ namespace osu.Game.Modes.Osu.Objects
internal int MaxCombo = 1; internal int MaxCombo = 1;
private float scalingFactor; private readonly float scalingFactor;
private float lazySliderLength; private float lazySliderLength;
private Vector2 startPosition; private readonly Vector2 startPosition;
private Vector2 endPosition; private readonly Vector2 endPosition;
internal OsuHitObjectDifficulty(OsuHitObject baseHitObject) internal OsuHitObjectDifficulty(OsuHitObject baseHitObject)
{ {

View File

@ -20,7 +20,7 @@ namespace osu.Game.Modes.Osu
private const float spin_radius = 50; private const float spin_radius = 50;
private Beatmap<OsuHitObject> beatmap; private readonly Beatmap<OsuHitObject> beatmap;
public OsuAutoReplay(Beatmap<OsuHitObject> beatmap) public OsuAutoReplay(Beatmap<OsuHitObject> beatmap)
{ {
@ -37,11 +37,11 @@ namespace osu.Game.Modes.Osu
} }
} }
private static IComparer<LegacyReplayFrame> replayFrameComparer = new LegacyReplayFrameComparer(); private static readonly IComparer<LegacyReplayFrame> replay_frame_comparer = new LegacyReplayFrameComparer();
private int findInsertionIndex(LegacyReplayFrame frame) private int findInsertionIndex(LegacyReplayFrame frame)
{ {
int index = Frames.BinarySearch(frame, replayFrameComparer); int index = Frames.BinarySearch(frame, replay_frame_comparer);
if (index < 0) if (index < 0)
{ {

View File

@ -12,6 +12,8 @@ using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Modes.Osu.Scoring;
using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Osu namespace osu.Game.Modes.Osu
{ {

View File

@ -1,7 +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 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Modes.Osu using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Osu.Scoring
{ {
internal class OsuScore : Score internal class OsuScore : Score
{ {

View File

@ -4,17 +4,18 @@
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Judgements;
using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Osu namespace osu.Game.Modes.Osu.Scoring
{ {
internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject, OsuJudgementInfo> internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject, OsuJudgement>
{ {
public OsuScoreProcessor() public OsuScoreProcessor()
{ {
} }
public OsuScoreProcessor(HitRenderer<OsuHitObject, OsuJudgementInfo> hitRenderer) public OsuScoreProcessor(HitRenderer<OsuHitObject, OsuJudgement> hitRenderer)
: base(hitRenderer) : base(hitRenderer)
{ {
} }
@ -27,7 +28,7 @@ namespace osu.Game.Modes.Osu
Accuracy.Value = 1; Accuracy.Value = 1;
} }
protected override void UpdateCalculations(OsuJudgementInfo judgement) protected override void OnNewJugement(OsuJudgement judgement)
{ {
if (judgement != null) if (judgement != null)
{ {
@ -47,9 +48,8 @@ namespace osu.Game.Modes.Osu
int score = 0; int score = 0;
int maxScore = 0; int maxScore = 0;
foreach (var judgementInfo in Judgements) foreach (var j in Judgements)
{ {
var j = judgementInfo;
score += j.ScoreValue; score += j.ScoreValue;
maxScore += j.MaxScoreValue; maxScore += j.MaxScoreValue;
} }

View File

@ -7,12 +7,14 @@ using osu.Game.Modes.Osu.Beatmaps;
using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Judgements;
using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Game.Modes.Osu.Scoring;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
namespace osu.Game.Modes.Osu.UI namespace osu.Game.Modes.Osu.UI
{ {
public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgementInfo> public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgement>
{ {
public OsuHitRenderer(WorkingBeatmap beatmap) public OsuHitRenderer(WorkingBeatmap beatmap)
: base(beatmap) : base(beatmap)
@ -25,11 +27,11 @@ namespace osu.Game.Modes.Osu.UI
protected override IBeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor(); protected override IBeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor();
protected override Playfield<OsuHitObject, OsuJudgementInfo> CreatePlayfield() => new OsuPlayfield(); protected override Playfield<OsuHitObject, OsuJudgement> CreatePlayfield() => new OsuPlayfield();
protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager(); protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager();
protected override DrawableHitObject<OsuHitObject, OsuJudgementInfo> GetVisualRepresentation(OsuHitObject h) protected override DrawableHitObject<OsuHitObject, OsuJudgement> GetVisualRepresentation(OsuHitObject h)
{ {
var circle = h as HitCircle; var circle = h as HitCircle;
if (circle != null) if (circle != null)

View File

@ -15,11 +15,11 @@ using osu.Game.Modes.Osu.Judgements;
namespace osu.Game.Modes.Osu.UI namespace osu.Game.Modes.Osu.UI
{ {
public class OsuPlayfield : Playfield<OsuHitObject, OsuJudgementInfo> public class OsuPlayfield : Playfield<OsuHitObject, OsuJudgement>
{ {
private Container approachCircles; private readonly Container approachCircles;
private Container judgementLayer; private readonly Container judgementLayer;
private ConnectionRenderer<OsuHitObject> connectionLayer; private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
public override Vector2 Size public override Vector2 Size
{ {
@ -65,7 +65,7 @@ namespace osu.Game.Modes.Osu.UI
AddInternal(new GameplayCursor()); AddInternal(new GameplayCursor());
} }
public override void Add(DrawableHitObject<OsuHitObject, OsuJudgementInfo> h) public override void Add(DrawableHitObject<OsuHitObject, OsuJudgement> h)
{ {
h.Depth = (float)h.HitObject.StartTime; h.Depth = (float)h.HitObject.StartTime;
@ -83,9 +83,13 @@ namespace osu.Game.Modes.Osu.UI
.OrderBy(h => h.StartTime); .OrderBy(h => h.StartTime);
} }
public override void OnJudgement(DrawableHitObject<OsuHitObject, OsuJudgementInfo> judgedObject) public override void OnJudgement(DrawableHitObject<OsuHitObject, OsuJudgement> judgedObject)
{ {
HitExplosion explosion = new HitExplosion(judgedObject.Judgement, judgedObject.HitObject); DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgedObject.Judgement)
{
Origin = Anchor.Centre,
Position = judgedObject.HitObject.StackedEndPosition + judgedObject.Judgement.PositionOffset
};
judgementLayer.Add(explosion); judgementLayer.Add(explosion);
} }

View File

@ -48,7 +48,7 @@
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" /> <Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
<Compile Include="Objects\Drawables\Connections\ConnectionRenderer.cs" /> <Compile Include="Objects\Drawables\Connections\ConnectionRenderer.cs" />
<Compile Include="Objects\Drawables\Connections\FollowPointRenderer.cs" /> <Compile Include="Objects\Drawables\Connections\FollowPointRenderer.cs" />
<Compile Include="Judgements\OsuJudgementInfo.cs" /> <Compile Include="Judgements\OsuJudgement.cs" />
<Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" /> <Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" />
<Compile Include="Objects\Drawables\Pieces\SpinnerBackground.cs" /> <Compile Include="Objects\Drawables\Pieces\SpinnerBackground.cs" />
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
@ -58,7 +58,7 @@
<Compile Include="Objects\Drawables\Pieces\ExplodePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\ExplodePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\FlashPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\FlashPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\GlowPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\GlowPiece.cs" />
<Compile Include="Objects\Drawables\HitExplosion.cs" /> <Compile Include="Objects\Drawables\DrawableOsuJudgement.cs" />
<Compile Include="Objects\Drawables\Pieces\NumberPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\NumberPiece.cs" />
<Compile Include="Objects\Drawables\DrawableSliderTick.cs" /> <Compile Include="Objects\Drawables\DrawableSliderTick.cs" />
<Compile Include="Objects\Drawables\Pieces\RingPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\RingPiece.cs" />
@ -72,8 +72,8 @@
<Compile Include="OsuAutoReplay.cs" /> <Compile Include="OsuAutoReplay.cs" />
<Compile Include="OsuDifficultyCalculator.cs" /> <Compile Include="OsuDifficultyCalculator.cs" />
<Compile Include="OsuKeyConversionInputManager.cs" /> <Compile Include="OsuKeyConversionInputManager.cs" />
<Compile Include="OsuScore.cs" /> <Compile Include="Scoring\OsuScore.cs" />
<Compile Include="OsuScoreProcessor.cs" /> <Compile Include="Scoring\OsuScoreProcessor.cs" />
<Compile Include="UI\OsuHitRenderer.cs" /> <Compile Include="UI\OsuHitRenderer.cs" />
<Compile Include="UI\OsuPlayfield.cs" /> <Compile Include="UI\OsuPlayfield.cs" />
<Compile Include="OsuRuleset.cs" /> <Compile Include="OsuRuleset.cs" />

View File

@ -2,18 +2,80 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Types;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Modes.Taiko.Beatmaps namespace osu.Game.Modes.Taiko.Beatmaps
{ {
internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoHitObject> internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoHitObject>
{ {
private const float legacy_velocity_scale = 1.4f;
private const float bash_convert_factor = 1.65f;
public Beatmap<TaikoHitObject> Convert(Beatmap original) public Beatmap<TaikoHitObject> Convert(Beatmap original)
{ {
if (original is LegacyBeatmap)
original.TimingInfo.ControlPoints.ForEach(c => c.VelocityAdjustment /= legacy_velocity_scale);
return new Beatmap<TaikoHitObject>(original) return new Beatmap<TaikoHitObject>(original)
{ {
HitObjects = new List<TaikoHitObject>() // Todo: Implement HitObjects = convertHitObjects(original.HitObjects)
};
}
private List<TaikoHitObject> convertHitObjects(List<HitObject> hitObjects)
{
return hitObjects.Select(convertHitObject).ToList();
}
private TaikoHitObject convertHitObject(HitObject original)
{
// Check if this HitObject is already a TaikoHitObject, and return it if so
TaikoHitObject originalTaiko = original as TaikoHitObject;
if (originalTaiko != null)
return originalTaiko;
IHasDistance distanceData = original as IHasDistance;
IHasRepeats repeatsData = original as IHasRepeats;
IHasEndTime endTimeData = original as IHasEndTime;
bool accented = ((original.Sample?.Type ?? SampleType.None) & SampleType.Finish) > 0;
if (distanceData != null)
{
return new DrumRoll
{
StartTime = original.StartTime,
Sample = original.Sample,
Accented = accented,
Distance = distanceData.Distance * (repeatsData?.RepeatCount ?? 1)
};
}
if (endTimeData != null)
{
// We compute the end time manually to add in the Bash convert factor
return new Bash
{
StartTime = original.StartTime,
Sample = original.Sample,
Accented = accented,
EndTime = original.StartTime + endTimeData.Duration * bash_convert_factor
};
}
return new Hit
{
StartTime = original.StartTime,
Sample = original.Sample,
Accented = accented
}; };
} }
} }

View File

@ -0,0 +1,34 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Modes.Taiko.Judgements
{
public class TaikoDrumRollTickJudgement : TaikoJudgement
{
/// <summary>
/// Drum roll ticks don't display judgement text.
/// </summary>
public override string ResultString => string.Empty;
/// <summary>
/// Drum roll ticks don't display judgement text.
/// </summary>
public override string MaxResultString => string.Empty;
protected override int NumericResultForScore(TaikoHitResult result)
{
switch (result)
{
default:
return 0;
case TaikoHitResult.Great:
return 200;
}
}
protected override int NumericResultForAccuracy(TaikoHitResult result)
{
return 0;
}
}
}

View File

@ -1,11 +1,15 @@
// 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 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Modes.Taiko.Judgements namespace osu.Game.Modes.Taiko.Judgements
{ {
public enum TaikoHitResult public enum TaikoHitResult
{ {
[Description("GOOD")]
Good, Good,
[Description("GREAT")]
Great Great
} }
} }

View File

@ -2,40 +2,45 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Judgements; using osu.Game.Modes.Judgements;
using osu.Framework.Extensions;
namespace osu.Game.Modes.Taiko.Judgements namespace osu.Game.Modes.Taiko.Judgements
{ {
public class TaikoJudgementInfo : JudgementInfo public class TaikoJudgement : Judgement
{ {
/// <summary> /// <summary>
/// The maximum score value. /// The maximum result.
/// </summary> /// </summary>
public const TaikoHitResult MAX_HIT_RESULT = TaikoHitResult.Great; public const TaikoHitResult MAX_HIT_RESULT = TaikoHitResult.Great;
/// <summary> /// <summary>
/// The score value. /// The result.
/// </summary> /// </summary>
public TaikoHitResult TaikoResult; public TaikoHitResult TaikoResult;
/// <summary> /// <summary>
/// The score value for the combo portion of the score. /// The result value for the combo portion of the score.
/// </summary> /// </summary>
public int ScoreValue => NumericResultForScore(TaikoResult); public int ResultValueForScore => NumericResultForScore(TaikoResult);
/// <summary> /// <summary>
/// The score value for the accuracy portion of the score. /// The result value for the accuracy portion of the score.
/// </summary> /// </summary>
public int AccuracyScoreValue => NumericResultForAccuracy(TaikoResult); public int ResultValueForAccuracy => NumericResultForAccuracy(TaikoResult);
/// <summary> /// <summary>
/// The maximum score value for the combo portion of the score. /// The maximum result value for the combo portion of the score.
/// </summary> /// </summary>
public int MaxScoreValue => NumericResultForScore(MAX_HIT_RESULT); public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
/// <summary> /// <summary>
/// The maximum score value for the accuracy portion of the score. /// The maximum result value for the accuracy portion of the score.
/// </summary> /// </summary>
public int MaxAccuracyScoreValue => NumericResultForAccuracy(MAX_HIT_RESULT); public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT);
public override string ResultString => TaikoResult.GetDescription();
public override string MaxResultString => MAX_HIT_RESULT.GetDescription();
/// <summary> /// <summary>
/// Whether this Judgement has a secondary hit in the case of finishers. /// Whether this Judgement has a secondary hit in the case of finishers.
@ -43,11 +48,11 @@ namespace osu.Game.Modes.Taiko.Judgements
public bool SecondHit; public bool SecondHit;
/// <summary> /// <summary>
/// Computes the numeric score value for the combo portion of the score. /// Computes the numeric result value for the combo portion of the score.
/// For the accuracy portion of the score (including accuracy percentage), see <see cref="NumericResultForAccuracy(TaikoHitResult)"/>. /// For the accuracy portion of the score (including accuracy percentage), see <see cref="NumericResultForAccuracy(TaikoHitResult)"/>.
/// </summary> /// </summary>
/// <param name="result">The result to compute the score value for.</param> /// <param name="result">The result to compute the value for.</param>
/// <returns>The numeric score value.</returns> /// <returns>The numeric result value.</returns>
protected virtual int NumericResultForScore(TaikoHitResult result) protected virtual int NumericResultForScore(TaikoHitResult result)
{ {
switch (result) switch (result)
@ -62,11 +67,11 @@ namespace osu.Game.Modes.Taiko.Judgements
} }
/// <summary> /// <summary>
/// Computes the numeric score value for the accuracy portion of the score. /// Computes the numeric result value for the accuracy portion of the score.
/// For the combo portion of the score, see <see cref="NumericResultForScore(TaikoHitResult)"/>. /// For the combo portion of the score, see <see cref="NumericResultForScore(TaikoHitResult)"/>.
/// </summary> /// </summary>
/// <param name="result">The result to compute the score value for.</param> /// <param name="result">The result to compute the value for.</param>
/// <returns>The numeric score value.</returns> /// <returns>The numeric result value.</returns>
protected virtual int NumericResultForAccuracy(TaikoHitResult result) protected virtual int NumericResultForAccuracy(TaikoHitResult result)
{ {
switch (result) switch (result)

View File

@ -0,0 +1,30 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Taiko.Objects
{
public class Bash : TaikoHitObject, IHasEndTime
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
/// <summary>
/// The number of hits required to complete the bash successfully.
/// </summary>
public int RequiredHits { get; protected set; }
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
double spinnerRotationRatio = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);
RequiredHits = (int)Math.Max(1, Duration / 1000f * spinnerRotationRatio);
}
}
}

View File

@ -1,39 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
internal class DrawableTaikoHit : Sprite
{
private TaikoHitObject h;
public DrawableTaikoHit(TaikoHitObject h)
{
this.h = h;
Origin = Anchor.Centre;
Scale = new Vector2(0.2f);
RelativePositionAxes = Axes.Both;
Position = new Vector2(1.1f, 0.5f);
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Texture = textures.Get(@"Menu/logo");
double duration = 0;
Transforms.Add(new TransformPositionX { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = 1.1f, EndValue = 0.1f });
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
Expire(true);
}
}
}

View File

@ -1,21 +1,14 @@
// 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 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgementInfo> public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgement>
{ {
/// <summary>
/// The colour used for various elements of this DrawableHitObject.
/// </summary>
public virtual Color4 AccentColour { get; }
protected DrawableTaikoHitObject(TaikoHitObject hitObject) protected DrawableTaikoHitObject(TaikoHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
@ -23,31 +16,24 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
Origin = Anchor.Centre; Origin = Anchor.Centre;
RelativePositionAxes = Axes.X; RelativePositionAxes = Axes.X;
}
protected override void LoadComplete()
{
LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2;
LifetimeEnd = HitObject.StartTime + HitObject.PreEmpt; LifetimeEnd = HitObject.StartTime + HitObject.PreEmpt;
// Todo: Remove (suppresses Resharper) base.LoadComplete();
AccentColour = Color4.White;
Children = new[]
{
CreateCircle()
};
} }
protected override TaikoJudgementInfo CreateJudgementInfo() => new TaikoJudgementInfo(); protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{
}
/// <summary> /// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between /// Sets the scroll position of the DrawableHitObject relative to the offset between
/// a time value and the HitObject's StartTime. /// a time value and the HitObject's StartTime.
/// </summary> /// </summary>
/// <param name="time"></param> /// <param name="time"></param>
protected void UpdateScrollPosition(double time) protected virtual void UpdateScrollPosition(double time)
{ {
MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt)); MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
} }

View File

@ -0,0 +1,104 @@
// 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.Samples;
using osu.Game.Modes.Objects.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
namespace osu.Game.Modes.Taiko.Objects
{
public class DrumRoll : TaikoHitObject, IHasDistance
{
public double EndTime => StartTime + Distance / Velocity;
public double Duration => EndTime - StartTime;
/// <summary>
/// Raw length of the drum roll in positional length units.
/// </summary>
public double Distance { get; set; }
/// <summary>
/// Velocity of the drum roll in positional length units per millisecond.
/// </summary>
public double Velocity { get; protected set; }
/// <summary>
/// The distance between ticks of this drumroll.
/// <para>Half of this value is the hit window of the ticks.</para>
/// </summary>
public double TickTimeDistance { get; protected set; }
/// <summary>
/// Number of drum roll ticks required for a "Good" hit.
/// </summary>
public double RequiredGoodHits { get; protected set; }
/// <summary>
/// Number of drum roll ticks required for a "Great" hit.
/// </summary>
public double RequiredGreatHits { get; protected set; }
/// <summary>
/// Total number of drum roll ticks.
/// </summary>
public int TotalTicks => Ticks.Count();
/// <summary>
/// Initializes the drum roll ticks if not initialized and returns them.
/// </summary>
public IEnumerable<DrumRollTick> Ticks => ticks ?? (ticks = createTicks());
private List<DrumRollTick> ticks;
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
Velocity = timing.SliderVelocityAt(StartTime) * difficulty.SliderMultiplier / 1000;
TickTimeDistance = timing.BeatLengthAt(StartTime);
//TODO: move this to legacy conversion code to allow for direct division without special case.
if (difficulty.SliderTickRate == 3)
TickTimeDistance /= 3;
else
TickTimeDistance /= 4;
RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
}
private List<DrumRollTick> createTicks()
{
var ret = new List<DrumRollTick>();
if (TickTimeDistance == 0)
return ret;
bool first = true;
for (double t = StartTime; t < EndTime + (int)TickTimeDistance; t += TickTimeDistance)
{
ret.Add(new DrumRollTick
{
FirstTick = first,
PreEmpt = PreEmpt,
TickTimeDistance = TickTimeDistance,
StartTime = t,
Sample = new HitSampleInfo
{
Type = SampleType.None,
Set = SampleSet.Soft
}
});
first = false;
}
return ret;
}
}
}

View File

@ -0,0 +1,19 @@
// 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.Modes.Taiko.Objects
{
public class DrumRollTick : TaikoHitObject
{
/// <summary>
/// Whether this is the first (initial) tick of the slider.
/// </summary>
public bool FirstTick;
/// <summary>
/// The distance between this tick and the next in milliseconds.
/// <para>Half of this value is the hit window of the tick.</para>
/// </summary>
public double TickTimeDistance;
}
}

View File

@ -0,0 +1,35 @@
// 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.Timing;
using osu.Game.Database;
namespace osu.Game.Modes.Taiko.Objects
{
public class Hit : TaikoHitObject
{
/// <summary>
/// The hit window that results in a "GREAT" hit.
/// </summary>
public double HitWindowGreat = 35;
/// <summary>
/// The hit window that results in a "GOOD" hit.
/// </summary>
public double HitWindowGood = 80;
/// <summary>
/// The hit window that results in a "MISS".
/// </summary>
public double HitWindowMiss = 95;
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
}
}
}

View File

@ -7,37 +7,28 @@ using osu.Game.Modes.Objects;
namespace osu.Game.Modes.Taiko.Objects namespace osu.Game.Modes.Taiko.Objects
{ {
public class TaikoHitObject : HitObject public abstract class TaikoHitObject : HitObject
{ {
/// <summary> /// <summary>
/// HitCircle radius. /// HitCircle radius.
/// </summary> /// </summary>
public const float CIRCLE_RADIUS = 64; public const float CIRCLE_RADIUS = 64;
/// <summary>
/// The hit window that results in a "GREAT" hit.
/// </summary>
public double HitWindowGreat = 35;
/// <summary>
/// The hit window that results in a "GOOD" hit.
/// </summary>
public double HitWindowGood = 80;
/// <summary>
/// The hit window that results in a "MISS".
/// </summary>
public double HitWindowMiss = 95;
/// <summary> /// <summary>
/// The time to scroll in the HitObject. /// The time to scroll in the HitObject.
/// </summary> /// </summary>
public double PreEmpt; public double PreEmpt;
/// <summary>
/// Whether this HitObject is accented.
/// Accented hit objects give more points for hitting the hit object with both keys.
/// </summary>
public bool Accented;
/// <summary> /// <summary>
/// Whether this HitObject is in Kiai time. /// Whether this HitObject is in Kiai time.
/// </summary> /// </summary>
public bool Kiai; public bool Kiai { get; protected set; }
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{ {
@ -50,10 +41,6 @@ namespace osu.Game.Modes.Taiko.Objects
if (overridePoint != null) if (overridePoint != null)
Kiai |= overridePoint.KiaiMode; Kiai |= overridePoint.KiaiMode;
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
} }
} }
} }

View File

@ -0,0 +1,263 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.UI;
using OpenTK;
namespace osu.Game.Modes.Taiko.Scoring
{
internal class TaikoScoreProcessor : ScoreProcessor<TaikoHitObject, TaikoJudgement>
{
/// <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 HP awarded by a <see cref="TaikoHitResult.Great"/> hit.
/// </summary>
private const double hp_hit_great = 0.03;
/// <summary>
/// The HP awarded for a <see cref="TaikoHitResult.Good"/> hit.
/// </summary>
private const double hp_hit_good = 0.011;
/// <summary>
/// The minimum HP deducted for a <see cref="HitResult.Miss"/>.
/// This occurs when HP Drain = 0.
/// </summary>
private const double hp_miss_min = -0.0018;
/// <summary>
/// The median HP deducted for a <see cref="HitResult.Miss"/>.
/// This occurs when HP Drain = 5.
/// </summary>
private const double hp_miss_mid = -0.0075;
/// <summary>
/// The maximum HP deducted for a <see cref="HitResult.Miss"/>.
/// This occurs when HP Drain = 10.
/// </summary>
private const double hp_miss_max = -0.12;
/// <summary>
/// The HP awarded for a <see cref="DrumRollTick"/> hit.
/// <para>
/// <see cref="DrumRollTick"/> hits award less HP as they're more spammable, although in hindsight
/// this probably awards too little HP and is kept at this value for now for compatibility.
/// </para>
/// </summary>
private const double hp_hit_tick = 0.00000003;
/// <summary>
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
/// </summary>
public override bool HasFailed => totalHits == maxTotalHits && Health.Value <= 0.5;
/// <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, 3.6) * 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 multiple of the original score added to the combo portion of the score
/// for correctly hitting an accented hit object with both keys.
/// </summary>
private double accentedHitScale;
private double hpIncreaseTick;
private double hpIncreaseGreat;
private double hpIncreaseGood;
private double hpIncreaseMiss;
private double maxComboPortion;
private double comboPortion;
private int maxTotalHits;
private int totalHits;
public TaikoScoreProcessor()
{
}
public TaikoScoreProcessor(HitRenderer<TaikoHitObject, TaikoJudgement> hitRenderer)
: base(hitRenderer)
{
}
protected override void ComputeTargets(Beatmap<TaikoHitObject> beatmap)
{
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98));
hpIncreaseTick = hp_hit_tick;
hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
hpIncreaseGood = hpMultiplierNormal * hp_hit_good;
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
var accentedHits = beatmap.HitObjects.FindAll(o => o is Hit && o.Accented);
// This is a linear function that awards:
// 10 times bonus points for hitting an accented hit object with both keys with 30 accented hit objects in the map
// 3 times bonus points for hitting an accented hit object with both keys with 120 accented hit objects in the map
accentedHitScale = -7d / 90d * MathHelper.Clamp(accentedHits.Count, 30, 120) + 111d / 9d;
foreach (var obj in beatmap.HitObjects)
{
if (obj is Hit)
{
AddJudgement(new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = TaikoHitResult.Great,
SecondHit = obj.Accented
});
}
else if (obj is DrumRoll)
{
for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++)
{
AddJudgement(new TaikoDrumRollTickJudgement
{
Result = HitResult.Hit,
TaikoResult = TaikoHitResult.Great,
SecondHit = obj.Accented
});
}
AddJudgement(new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = TaikoHitResult.Great,
SecondHit = obj.Accented
});
}
else if (obj is Bash)
{
AddJudgement(new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = TaikoHitResult.Great
});
}
}
maxTotalHits = totalHits;
maxComboPortion = comboPortion;
}
protected override void OnNewJugement(TaikoJudgement judgement)
{
bool isTick = judgement is TaikoDrumRollTickJudgement;
// Don't consider ticks as a type of hit that counts towards map completion
if (!isTick)
totalHits++;
// Apply score changes
if (judgement.Result == HitResult.Hit)
{
double baseValue = judgement.ResultValueForScore;
// Add bonus points for hitting an accented hit object with the second key
if (judgement.SecondHit)
baseValue += baseValue * accentedHitScale;
// Add score to portions
if (isTick)
bonusScore += baseValue;
else
{
Combo.Value++;
// A relevance factor that needs to be applied to make higher combos more relevant
// Value is capped at 400 combo
double comboRelevance = Math.Min(Math.Log(400, combo_base), Math.Max(0.5, Math.Log(Combo.Value, combo_base)));
comboPortion += baseValue * comboRelevance;
}
}
// Apply HP changes
switch (judgement.Result)
{
case HitResult.Miss:
// Missing ticks shouldn't drop HP
if (!isTick)
Health.Value += hpIncreaseMiss;
break;
case HitResult.Hit:
switch (judgement.TaikoResult)
{
case TaikoHitResult.Good:
Health.Value += hpIncreaseGood;
break;
case TaikoHitResult.Great:
if (isTick)
Health.Value += hpIncreaseTick;
else
Health.Value += hpIncreaseGreat;
break;
}
break;
}
// Compute the new score + accuracy
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()
{
base.Reset();
Health.Value = 0;
bonusScore = 0;
comboPortion = 0;
totalHits = 0;
}
}
}

View File

@ -10,6 +10,8 @@ using osu.Game.Modes.Taiko.UI;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.Taiko.Scoring;
namespace osu.Game.Modes.Taiko namespace osu.Game.Modes.Taiko
{ {

View File

@ -1,25 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.UI;
namespace osu.Game.Modes.Taiko
{
internal class TaikoScoreProcessor : ScoreProcessor<TaikoHitObject, TaikoJudgementInfo>
{
public TaikoScoreProcessor()
{
}
public TaikoScoreProcessor(HitRenderer<TaikoHitObject, TaikoJudgementInfo> hitRenderer)
: base(hitRenderer)
{
}
protected override void UpdateCalculations(TaikoJudgementInfo newJudgement)
{
}
}
}

View File

@ -0,0 +1,57 @@
// 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.Modes.Taiko.Judgements;
using osu.Game.Modes.Objects.Drawables;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Taiko.UI
{
/// <summary>
/// Text that is shown as judgement when a hit object is hit or missed.
/// </summary>
public class DrawableTaikoJudgement : DrawableJudgement<TaikoJudgement>
{
/// <summary>
/// Creates a new judgement text.
/// </summary>
/// <param name="judgement">The judgement to visualise.</param>
public DrawableTaikoJudgement(TaikoJudgement judgement)
: base(judgement)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
switch (Judgement.Result)
{
case HitResult.Hit:
switch (Judgement.TaikoResult)
{
case TaikoHitResult.Good:
Colour = colours.GreenLight;
break;
case TaikoHitResult.Great:
Colour = colours.BlueLight;
break;
}
break;
}
}
protected override void LoadComplete()
{
switch (Judgement.Result)
{
case HitResult.Hit:
MoveToY(-100, 500);
break;
}
base.LoadComplete();
}
}
}

View File

@ -0,0 +1,78 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects;
namespace osu.Game.Modes.Taiko.UI
{
/// <summary>
/// A circle explodes from the hit target to indicate a hitobject has been hit.
/// </summary>
internal class HitExplosion : CircularContainer
{
private readonly TaikoJudgement judgement;
private readonly Box innerFill;
public HitExplosion(TaikoJudgement judgement)
{
this.judgement = judgement;
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.Both;
BorderColour = Color4.White;
BorderThickness = 1;
Alpha = 0.15f;
Masking = true;
Children = new[]
{
innerFill = new Box
{
RelativeSizeAxes = Axes.Both,
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (judgement.SecondHit)
Size *= 1.5f;
switch (judgement.TaikoResult)
{
case TaikoHitResult.Good:
innerFill.Colour = colours.Green;
break;
case TaikoHitResult.Great:
innerFill.Colour = colours.Blue;
break;
}
}
protected override void LoadComplete()
{
base.LoadComplete();
ScaleTo(5f, 1000, EasingTypes.OutQuint);
FadeOut(500);
Expire();
}
}
}

View File

@ -0,0 +1,105 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Modes.Taiko.Objects;
namespace osu.Game.Modes.Taiko.UI
{
/// <summary>
/// A component that is displayed at the hit position in the taiko playfield.
/// </summary>
internal class HitTarget : Container
{
/// <summary>
/// Diameter of normal hit object circles.
/// </summary>
private const float normal_diameter = TaikoHitObject.CIRCLE_RADIUS * 2 * TaikoPlayfield.PLAYFIELD_SCALE;
/// <summary>
/// Diameter of finisher hit object circles.
/// </summary>
private const float finisher_diameter = normal_diameter * 1.5f;
/// <summary>
/// The 1px inner border of the taiko playfield.
/// </summary>
private const float border_offset = 1;
/// <summary>
/// Thickness of all drawn line pieces.
/// </summary>
private const float border_thickness = 2.5f;
public HitTarget()
{
RelativeSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Box
{
Name = "Bar Upper",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = border_offset,
Size = new Vector2(border_thickness, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset),
Alpha = 0.1f
},
new CircularContainer
{
Name = "Finisher Ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(finisher_diameter),
Masking = true,
BorderColour = Color4.White,
BorderThickness = border_thickness,
Alpha = 0.1f,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
new CircularContainer
{
Name = "Normal Ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(normal_diameter),
Masking = true,
BorderColour = Color4.White,
BorderThickness = border_thickness,
Alpha = 0.5f,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
new Box
{
Name = "Bar Lower",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Y = -border_offset,
Size = new Vector2(border_thickness, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset),
Alpha = 0.1f
},
};
}
}
}

View File

@ -0,0 +1,149 @@
// 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.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
namespace osu.Game.Modes.Taiko.UI
{
/// <summary>
/// A component of the playfield that captures input and displays input as a drum.
/// </summary>
internal class InputDrum : Container
{
public InputDrum()
{
Size = new Vector2(TaikoPlayfield.PlayfieldHeight);
const float middle_split = 10;
Children = new Drawable[]
{
new TaikoHalfDrum(false)
{
Name = "Left Half",
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Both,
X = -middle_split / 2,
RimKey = Key.D,
CentreKey = Key.F
},
new TaikoHalfDrum(true)
{
Name = "Right Half",
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
X = middle_split / 2,
Position = new Vector2(-1f, 0),
RimKey = Key.K,
CentreKey = Key.J
}
};
}
/// <summary>
/// A half-drum. Contains one centre and one rim hit.
/// </summary>
private class TaikoHalfDrum : Container
{
/// <summary>
/// The key to be used for the rim of the half-drum.
/// </summary>
public Key RimKey;
/// <summary>
/// The key to be used for the centre of the half-drum.
/// </summary>
public Key CentreKey;
private readonly Sprite rim;
private readonly Sprite rimHit;
private readonly Sprite centre;
private readonly Sprite centreHit;
public TaikoHalfDrum(bool flipped)
{
Masking = true;
Children = new Drawable[]
{
rim = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both
},
rimHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
},
centre = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f)
},
centreHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f),
Alpha = 0,
BlendingMode = BlendingMode.Additive
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures, OsuColour colours)
{
rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer");
rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit");
centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner");
centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit");
rimHit.Colour = colours.Blue;
centreHit.Colour = colours.Pink;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat)
return false;
Drawable target = null;
if (args.Key == CentreKey)
target = centreHit;
else if (args.Key == RimKey)
target = rimHit;
if (target != null)
{
target.FadeTo(Math.Min(target.Alpha + 0.4f, 1), 40, EasingTypes.OutQuint);
target.Delay(40);
target.FadeOut(600, EasingTypes.OutQuint);
}
return false;
}
}
}
}

View File

@ -3,14 +3,16 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.Taiko.Beatmaps; using osu.Game.Modes.Taiko.Beatmaps;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.Taiko.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Taiko.UI namespace osu.Game.Modes.Taiko.UI
{ {
public class TaikoHitRenderer : HitRenderer<TaikoHitObject, TaikoJudgementInfo> public class TaikoHitRenderer : HitRenderer<TaikoHitObject, TaikoJudgement>
{ {
public TaikoHitRenderer(WorkingBeatmap beatmap) public TaikoHitRenderer(WorkingBeatmap beatmap)
: base(beatmap) : base(beatmap)
@ -23,8 +25,8 @@ namespace osu.Game.Modes.Taiko.UI
protected override IBeatmapProcessor<TaikoHitObject> CreateBeatmapProcessor() => new TaikoBeatmapProcessor(); protected override IBeatmapProcessor<TaikoHitObject> CreateBeatmapProcessor() => new TaikoBeatmapProcessor();
protected override Playfield<TaikoHitObject, TaikoJudgementInfo> CreatePlayfield() => new TaikoPlayfield(); protected override Playfield<TaikoHitObject, TaikoJudgement> CreatePlayfield() => new TaikoPlayfield();
protected override DrawableHitObject<TaikoHitObject, TaikoJudgementInfo> GetVisualRepresentation(TaikoHitObject h) => null; protected override DrawableHitObject<TaikoHitObject, TaikoJudgement> GetVisualRepresentation(TaikoHitObject h) => null;
} }
} }

View File

@ -4,38 +4,192 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Modes.Taiko.UI namespace osu.Game.Modes.Taiko.UI
{ {
public class TaikoPlayfield : Playfield<TaikoHitObject, TaikoJudgementInfo> public class TaikoPlayfield : Playfield<TaikoHitObject, TaikoJudgement>
{ {
/// <summary>
/// The default play field height.
/// </summary>
public const float PLAYFIELD_BASE_HEIGHT = 242;
/// <summary>
/// The play field height scale.
/// </summary>
public const float PLAYFIELD_SCALE = 0.65f;
/// <summary>
/// The play field height after scaling.
/// </summary>
public static float PlayfieldHeight => PLAYFIELD_BASE_HEIGHT * PLAYFIELD_SCALE;
/// <summary>
/// The offset from <see cref="left_area_size"/> which the center of the hit target lies at.
/// </summary>
private const float hit_target_offset = 80;
/// <summary>
/// The size of the left area of the playfield. This area contains the input drum.
/// </summary>
private const float left_area_size = 240;
protected override Container<Drawable> Content => hitObjectContainer;
private readonly Container<HitExplosion> hitExplosionContainer;
//private Container<DrawableBarLine> barLineContainer;
private readonly Container<DrawableTaikoJudgement> judgementContainer;
private readonly Container hitObjectContainer;
//private Container topLevelHitContainer;
private readonly Container leftBackgroundContainer;
private readonly Container rightBackgroundContainer;
private readonly Box leftBackground;
private readonly Box rightBackground;
public TaikoPlayfield() public TaikoPlayfield()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Size = new Vector2(1, 100); Height = PlayfieldHeight;
Anchor = Anchor.Centre;
Origin = Anchor.Centre; AddInternal(new Drawable[]
{
rightBackgroundContainer = new Container
{
RelativeSizeAxes = Axes.Both,
BorderThickness = 2,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.2f),
Radius = 5,
},
Children = new Drawable[]
{
rightBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.6f
},
}
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = left_area_size },
Children = new Drawable[]
{
new Container
{
Padding = new MarginPadding { Left = hit_target_offset },
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
hitExplosionContainer = new Container<HitExplosion>
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
Scale = new Vector2(PLAYFIELD_SCALE),
BlendingMode = BlendingMode.Additive
},
//barLineContainer = new Container<DrawableBarLine>
//{
// RelativeSizeAxes = Axes.Both,
//},
new HitTarget
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
},
hitObjectContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
judgementContainer = new Container<DrawableTaikoJudgement>
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive
},
},
},
}
},
leftBackgroundContainer = new Container
{
Size = new Vector2(left_area_size, PlayfieldHeight),
BorderThickness = 1,
Children = new Drawable[]
{
leftBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
new InputDrum
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.X,
Position = new Vector2(0.10f, 0),
Scale = new Vector2(0.9f)
},
new Box
{
Anchor = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 10,
ColourInfo = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
},
}
},
//topLevelHitContainer = new Container
//{
// RelativeSizeAxes = Axes.Both,
//}
});
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(TextureStore textures) private void load(OsuColour colours)
{ {
Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f }); leftBackgroundContainer.BorderColour = colours.Gray0;
leftBackground.Colour = colours.Gray1;
Add(new Sprite rightBackgroundContainer.BorderColour = colours.Gray1;
rightBackground.Colour = colours.Gray0;
}
public override void Add(DrawableHitObject<TaikoHitObject, TaikoJudgement> h)
{
h.Depth = (float)h.HitObject.StartTime;
base.Add(h);
}
public override void OnJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> judgedObject)
{
bool wasHit = judgedObject.Judgement.Result == HitResult.Hit;
if (wasHit)
hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement));
judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement)
{ {
Texture = textures.Get(@"Menu/logo"), Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft,
Origin = Anchor.Centre, Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre,
Scale = new Vector2(0.2f), RelativePositionAxes = Axes.X,
RelativePositionAxes = Axes.Both, X = wasHit ? judgedObject.Position.X : 0,
Position = new Vector2(0.1f, 0.5f),
Colour = Color4.Gray
}); });
} }
} }

View File

@ -49,8 +49,14 @@
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\TaikoBeatmapConverter.cs" /> <Compile Include="Beatmaps\TaikoBeatmapConverter.cs" />
<Compile Include="Beatmaps\TaikoBeatmapProcessor.cs" /> <Compile Include="Beatmaps\TaikoBeatmapProcessor.cs" />
<Compile Include="Judgements\TaikoJudgementInfo.cs" /> <Compile Include="Judgements\TaikoDrumRollTickJudgement.cs" />
<Compile Include="Judgements\TaikoJudgement.cs" />
<Compile Include="Judgements\TaikoHitResult.cs" /> <Compile Include="Judgements\TaikoHitResult.cs" />
<Compile Include="Objects\Drawable\DrawableTaikoHitObject.cs" />
<Compile Include="Objects\Bash.cs" />
<Compile Include="Objects\DrumRoll.cs" />
<Compile Include="Objects\DrumRollTick.cs" />
<Compile Include="Objects\Hit.cs" />
<Compile Include="Objects\Drawable\Pieces\BashCirclePiece.cs" /> <Compile Include="Objects\Drawable\Pieces\BashCirclePiece.cs" />
<Compile Include="Objects\Drawable\Pieces\CentreHitCirclePiece.cs" /> <Compile Include="Objects\Drawable\Pieces\CentreHitCirclePiece.cs" />
<Compile Include="Objects\Drawable\Pieces\CirclePiece.cs" /> <Compile Include="Objects\Drawable\Pieces\CirclePiece.cs" />
@ -59,10 +65,13 @@
<Compile Include="Objects\Drawable\Pieces\RimHitCirclePiece.cs" /> <Compile Include="Objects\Drawable\Pieces\RimHitCirclePiece.cs" />
<Compile Include="Objects\Drawable\Pieces\ScrollingCirclePiece.cs" /> <Compile Include="Objects\Drawable\Pieces\ScrollingCirclePiece.cs" />
<Compile Include="TaikoDifficultyCalculator.cs" /> <Compile Include="TaikoDifficultyCalculator.cs" />
<Compile Include="Objects\Drawable\DrawableTaikoHit.cs" />
<Compile Include="Objects\TaikoHitObject.cs" /> <Compile Include="Objects\TaikoHitObject.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TaikoScoreProcessor.cs" /> <Compile Include="Scoring\TaikoScoreProcessor.cs" />
<Compile Include="UI\HitTarget.cs" />
<Compile Include="UI\InputDrum.cs" />
<Compile Include="UI\DrawableTaikoJudgement.cs" />
<Compile Include="UI\HitExplosion.cs" />
<Compile Include="UI\TaikoHitRenderer.cs" /> <Compile Include="UI\TaikoHitRenderer.cs" />
<Compile Include="UI\TaikoPlayfield.cs" /> <Compile Include="UI\TaikoPlayfield.cs" />
<Compile Include="TaikoRuleset.cs" /> <Compile Include="TaikoRuleset.cs" />
@ -80,10 +89,6 @@
<Project>{C76BF5B3-985E-4D39-95FE-97C9C879B83A}</Project> <Project>{C76BF5B3-985E-4D39-95FE-97C9C879B83A}</Project>
<Name>osu.Framework</Name> <Name>osu.Framework</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\osu.Game.Modes.Osu\osu.Game.Modes.Osu.csproj">
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Modes.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project> <Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project>
<Name>osu.Game</Name> <Name>osu.Game</Name>

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(File.Exists(temp)); Assert.IsTrue(File.Exists(temp));
var importer = new BeatmapIPCChannel(client); var importer = new BeatmapIPCChannel(client);
if (!importer.ImportAsync(temp).Wait(1000)) if (!importer.ImportAsync(temp).Wait(5000))
Assert.Fail(@"IPC took too long to send"); Assert.Fail(@"IPC took too long to send");
ensureLoaded(osu); ensureLoaded(osu);

View File

@ -15,8 +15,8 @@ namespace osu.Game.Beatmaps
private void loadTiming() private void loadTiming()
{ {
// TODO: Handle mods // TODO: Handle mods
int audioRate = 100; const int audio_rate = 100;
TimeRate = audioRate / 100.0; TimeRate = audio_rate / 100.0;
} }
public double Calculate(Dictionary<string, string> categoryDifficulty = null) public double Calculate(Dictionary<string, string> categoryDifficulty = null)

View File

@ -21,12 +21,12 @@ namespace osu.Game.Beatmaps.Drawables
public class BeatmapPanel : Panel public class BeatmapPanel : Panel
{ {
public BeatmapInfo Beatmap; public BeatmapInfo Beatmap;
private Sprite background; private readonly Sprite background;
public Action<BeatmapPanel> GainedSelection; public Action<BeatmapPanel> GainedSelection;
public Action<BeatmapPanel> StartRequested; public Action<BeatmapPanel> StartRequested;
private Triangles triangles; private readonly Triangles triangles;
private StarCounter starCounter; private readonly StarCounter starCounter;
protected override void Selected() protected override void Selected()
{ {

View File

@ -20,11 +20,12 @@ namespace osu.Game.Beatmaps.Drawables
public class BeatmapSetHeader : Panel public class BeatmapSetHeader : Panel
{ {
public Action<BeatmapSetHeader> GainedSelection; public Action<BeatmapSetHeader> GainedSelection;
private SpriteText title, artist; private readonly SpriteText title;
private readonly SpriteText artist;
private OsuConfigManager config; private OsuConfigManager config;
private Bindable<bool> preferUnicode; private Bindable<bool> preferUnicode;
private WorkingBeatmap beatmap; private readonly WorkingBeatmap beatmap;
private FillFlowContainer difficultyIcons; private readonly FillFlowContainer difficultyIcons;
public BeatmapSetHeader(WorkingBeatmap beatmap) public BeatmapSetHeader(WorkingBeatmap beatmap)
{ {

View File

@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps.Drawables
public override bool RemoveWhenNotAlive => false; public override bool RemoveWhenNotAlive => false;
private Container nestedContainer; private readonly Container nestedContainer;
protected override Container<Drawable> Content => nestedContainer; protected override Container<Drawable> Content => nestedContainer;

View File

@ -21,9 +21,9 @@ namespace osu.Game.Beatmaps.IO
OsuLegacyDecoder.Register(); OsuLegacyDecoder.Register();
} }
private Stream archiveStream; private readonly Stream archiveStream;
private ZipFile archive; private readonly ZipFile archive;
private Beatmap firstMap; private readonly Beatmap firstMap;
public OszArchiveReader(Stream archiveStream) public OszArchiveReader(Stream archiveStream)
{ {

View File

@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps
protected abstract Track GetTrack(); protected abstract Track GetTrack();
private Beatmap beatmap; private Beatmap beatmap;
private object beatmapLock = new object(); private readonly object beatmapLock = new object();
public Beatmap Beatmap public Beatmap Beatmap
{ {
get get
@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps
} }
} }
private object backgroundLock = new object(); private readonly object backgroundLock = new object();
private Texture background; private Texture background;
public Texture Background public Texture Background
{ {
@ -68,7 +68,7 @@ namespace osu.Game.Beatmaps
} }
private Track track; private Track track;
private object trackLock = new object(); private readonly object trackLock = new object();
public Track Track public Track Track
{ {
get get

View File

@ -21,7 +21,7 @@ namespace osu.Game.Database
public class BeatmapDatabase public class BeatmapDatabase
{ {
private SQLiteConnection connection { get; } private SQLiteConnection connection { get; }
private Storage storage; private readonly Storage storage;
public event Action<BeatmapSetInfo> BeatmapSetAdded; public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved; public event Action<BeatmapSetInfo> BeatmapSetRemoved;

View File

@ -8,6 +8,7 @@ using osu.Framework.Platform;
using osu.Game.IO.Legacy; using osu.Game.IO.Legacy;
using osu.Game.IPC; using osu.Game.IPC;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Scoring;
using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.LZMA;
namespace osu.Game.Database namespace osu.Game.Database

View File

@ -14,7 +14,7 @@ namespace osu.Game.Graphics.Backgrounds
{ {
public Sprite Sprite; public Sprite Sprite;
private string textureName; private readonly string textureName;
public Background(string textureName = @"") public Background(string textureName = @"")
{ {

View File

@ -89,13 +89,13 @@ namespace osu.Game.Graphics.Backgrounds
protected virtual Triangle CreateTriangle() protected virtual Triangle CreateTriangle()
{ {
float stdDev = 0.16f; const float std_dev = 0.16f;
float mean = 0.5f; const float mean = 0.5f;
float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats
float u2 = 1 - RNG.NextSingle(); float u2 = 1 - RNG.NextSingle();
float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) 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 + stdDev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2)
const float size = 100; const float size = 100;

View File

@ -12,7 +12,7 @@ using osu.Framework.Configuration;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
{ {
internal class ParallaxContainer : Container internal class ParallaxContainer : Container, IRequireHighFrequencyMousePosition
{ {
public float ParallaxAmount = 0.02f; public float ParallaxAmount = 0.02f;
@ -31,7 +31,7 @@ namespace osu.Game.Graphics.Containers
}); });
} }
private Container content; private readonly Container content;
private InputManager input; private InputManager input;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;

View File

@ -31,10 +31,10 @@ namespace osu.Game.Graphics.Cursor
private float time; private float time;
private TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
private const int max_sprites = 2048; private const int max_sprites = 2048;
private TrailPart[] parts = new TrailPart[max_sprites]; private readonly TrailPart[] parts = new TrailPart[max_sprites];
private Vector2? lastPosition; private Vector2? lastPosition;
@ -88,10 +88,10 @@ namespace osu.Game.Graphics.Cursor
Invalidate(Invalidation.DrawNode, shallPropagate: false); Invalidate(Invalidation.DrawNode, shallPropagate: false);
int fadeClockResetThreshold = 1000000; const int fade_clock_reset_threshold = 1000000;
time = (float)(Time.Current - timeOffset) / 500f; time = (float)(Time.Current - timeOffset) / 500f;
if (time > fadeClockResetThreshold) if (time > fade_clock_reset_threshold)
resetTime(); resetTime();
} }
@ -163,7 +163,7 @@ namespace osu.Game.Graphics.Cursor
public float Time; public float Time;
public TrailDrawNodeSharedData Shared; public TrailDrawNodeSharedData Shared;
public TrailPart[] Parts = new TrailPart[max_sprites]; public readonly TrailPart[] Parts = new TrailPart[max_sprites];
public Vector2 Size; public Vector2 Size;
public TrailDrawNode() public TrailDrawNode()

View File

@ -81,9 +81,14 @@ namespace osu.Game.Graphics.UserInterface
public SampleChannel SampleClick, SampleHover; public SampleChannel SampleClick, SampleHover;
private Container backgroundContainer, colourContainer, glowContainer; private readonly Container backgroundContainer;
private Box leftGlow, centerGlow, rightGlow, background; private readonly Container colourContainer;
private SpriteText spriteText; private readonly Container glowContainer;
private readonly Box leftGlow;
private readonly Box centerGlow;
private readonly Box rightGlow;
private readonly Box background;
private readonly SpriteText spriteText;
private Vector2 hoverSpacing => new Vector2(3f, 0f); private Vector2 hoverSpacing => new Vector2(3f, 0f);
private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking

View File

@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
public const float COLLAPSED_SIZE = 20; public const float COLLAPSED_SIZE = 20;
public const float EXPANDED_SIZE = 40; public const float EXPANDED_SIZE = 40;
private Box fill; private readonly Box fill;
private const float border_width = 3; private const float border_width = 3;
private Color4 glowingColour, idleColour; private Color4 glowingColour, idleColour;

View File

@ -64,8 +64,8 @@ namespace osu.Game.Graphics.UserInterface
} }
} }
private Nub nub; private readonly Nub nub;
private SpriteText labelSpriteText; private readonly SpriteText labelSpriteText;
private SampleChannel sampleChecked; private SampleChannel sampleChecked;
private SampleChannel sampleUnchecked; private SampleChannel sampleUnchecked;

View File

@ -85,7 +85,7 @@ namespace osu.Game.Graphics.UserInterface
private Color4? accentColour; private Color4? accentColour;
private TextAwesome chevron; private readonly TextAwesome chevron;
protected override void FormatForeground(bool hover = false) protected override void FormatForeground(bool hover = false)
{ {
@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterface
protected class OsuDropdownHeader : DropdownHeader protected class OsuDropdownHeader : DropdownHeader
{ {
private SpriteText label; private readonly SpriteText label;
protected override string Label protected override string Label
{ {
get { return label.Text; } get { return label.Text; }

View File

@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
public class PasswordMaskChar : Container public class PasswordMaskChar : Container
{ {
private CircularContainer circle; private readonly CircularContainer circle;
public PasswordMaskChar(float size) public PasswordMaskChar(float size)
{ {

View File

@ -19,8 +19,9 @@ namespace osu.Game.Graphics.UserInterface
private SampleChannel sample; private SampleChannel sample;
private double lastSampleTime; private double lastSampleTime;
private Nub nub; private readonly Nub nub;
private Box leftBox, rightBox; private readonly Box leftBox;
private readonly Box rightBox;
public OsuSliderBar() public OsuSliderBar()
{ {

View File

@ -28,6 +28,8 @@ namespace osu.Game.Graphics.UserInterface
public OsuTabControl() public OsuTabControl()
{ {
TabContainer.Spacing = new Vector2(10f, 0f);
if (!typeof(T).IsEnum) if (!typeof(T).IsEnum)
throw new InvalidOperationException("OsuTabControl only supports enums as the generic type argument"); throw new InvalidOperationException("OsuTabControl only supports enums as the generic type argument");
@ -59,8 +61,8 @@ namespace osu.Game.Graphics.UserInterface
private class OsuTabItem : TabItem<T> private class OsuTabItem : TabItem<T>
{ {
private SpriteText text; private readonly SpriteText text;
private Box box; private readonly Box box;
private Color4? accentColour; private Color4? accentColour;
public Color4 AccentColour public Color4 AccentColour
@ -142,7 +144,7 @@ namespace osu.Game.Graphics.UserInterface
{ {
text = new OsuSpriteText text = new OsuSpriteText
{ {
Margin = new MarginPadding(5), Margin = new MarginPadding { Top = 5, Bottom = 5 },
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
TextSize = 14, TextSize = 14,

View File

@ -0,0 +1,140 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
{
/// <summary>
/// A checkbox styled to be placed in line with an <see cref="OsuTabControl{T}"/>
/// </summary>
public class OsuTabControlCheckBox : CheckBox
{
private readonly Box box;
private readonly SpriteText text;
private readonly TextAwesome icon;
public event EventHandler<CheckBoxState> Action;
private Color4? accentColour;
public Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
set
{
accentColour = value;
if (State != CheckBoxState.Checked)
{
text.Colour = AccentColour;
icon.Colour = AccentColour;
}
}
}
public string Text
{
get { return text.Text; }
set { text.Text = value; }
}
protected override void OnChecked()
{
fadeIn();
icon.Icon = FontAwesome.fa_check_circle_o;
Action?.Invoke(this, State);
}
protected override void OnUnchecked()
{
fadeOut();
icon.Icon = FontAwesome.fa_circle_o;
Action?.Invoke(this, State);
}
private const float transition_length = 500;
private void fadeIn()
{
box.FadeIn(transition_length, EasingTypes.OutQuint);
text.FadeColour(Color4.White, transition_length, EasingTypes.OutQuint);
}
private void fadeOut()
{
box.FadeOut(transition_length, EasingTypes.OutQuint);
text.FadeColour(AccentColour, transition_length, EasingTypes.OutQuint);
}
protected override bool OnHover(InputState state)
{
fadeIn();
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
if (State == CheckBoxState.Unchecked)
fadeOut();
base.OnHoverLost(state);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (accentColour == null)
AccentColour = colours.Blue;
}
public OsuTabControlCheckBox()
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Top = 5, Bottom = 5, },
Spacing = new Vector2(5f, 0f),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
text = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
},
icon = new TextAwesome
{
TextSize = 14,
Icon = FontAwesome.fa_circle_o,
Shadow = true,
},
},
},
box = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Alpha = 0,
Colour = Color4.White,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
}
};
}
}
}

View File

@ -147,7 +147,7 @@ namespace osu.Game.Graphics.UserInterface
private class Star : Container private class Star : Container
{ {
public TextAwesome Icon; public readonly TextAwesome Icon;
public Star() public Star()
{ {
Size = new Vector2(star_size); Size = new Vector2(star_size);

View File

@ -16,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface
{ {
public class TwoLayerButton : ClickableContainer public class TwoLayerButton : ClickableContainer
{ {
private TextAwesome icon; private readonly TextAwesome icon;
public Box IconLayer; public Box IconLayer;
public Box TextLayer; public Box TextLayer;
@ -29,11 +29,11 @@ namespace osu.Game.Graphics.UserInterface
public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50); public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50);
public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50); public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50);
public SampleChannel ActivationSound; public SampleChannel ActivationSound;
private SpriteText text; private readonly SpriteText text;
public Color4 HoverColour; public Color4 HoverColour;
private Container c1; private readonly Container c1;
private Container c2; private readonly Container c2;
public Color4 BackgroundColour public Color4 BackgroundColour
{ {
@ -171,7 +171,7 @@ namespace osu.Game.Graphics.UserInterface
IconLayer.FadeColour(HoverColour, transform_time, EasingTypes.OutElastic); IconLayer.FadeColour(HoverColour, transform_time, EasingTypes.OutElastic);
double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
double startTime = Time.Current + offset; double startTime = Time.Current + offset;
// basic pulse // basic pulse
@ -200,7 +200,7 @@ namespace osu.Game.Graphics.UserInterface
int duration = 0; //(int)(Game.Audio.BeatLength); int duration = 0; //(int)(Game.Audio.BeatLength);
if (duration == 0) duration = pulse_length * 2; if (duration == 0) duration = pulse_length * 2;
double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
double startTime = Time.Current + offset; double startTime = Time.Current + offset;
// slow pulse // slow pulse

View File

@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
{ {
internal class VolumeControl : OverlayContainer internal class VolumeControl : OverlayContainer
{ {
private VolumeMeter volumeMeterMaster; private readonly VolumeMeter volumeMeterMaster;
protected override bool HideOnEscape => false; protected override bool HideOnEscape => false;
@ -89,8 +89,8 @@ namespace osu.Game.Graphics.UserInterface.Volume
private ScheduledDelegate popOutDelegate; private ScheduledDelegate popOutDelegate;
private VolumeMeter volumeMeterEffect; private readonly VolumeMeter volumeMeterEffect;
private VolumeMeter volumeMeterMusic; private readonly VolumeMeter volumeMeterMusic;
protected override void PopIn() protected override void PopIn()
{ {

View File

@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
{ {
internal class VolumeMeter : Container internal class VolumeMeter : Container
{ {
private Box meterFill; private readonly Box meterFill;
public BindableDouble Bindable { get; } = new BindableDouble(); public BindableDouble Bindable { get; } = new BindableDouble();
public VolumeMeter(string meterName) public VolumeMeter(string meterName)

View File

@ -18,7 +18,7 @@ namespace osu.Game.IO.Legacy
/// handle null strings and simplify use with ISerializable. </summary> /// handle null strings and simplify use with ISerializable. </summary>
public class SerializationReader : BinaryReader public class SerializationReader : BinaryReader
{ {
private Stream stream; private readonly Stream stream;
public SerializationReader(Stream s) public SerializationReader(Stream s)
: base(s, Encoding.UTF8) : base(s, Encoding.UTF8)

View File

@ -10,7 +10,7 @@ namespace osu.Game.IPC
{ {
public class BeatmapIPCChannel : IpcChannel<BeatmapImportMessage> public class BeatmapIPCChannel : IpcChannel<BeatmapImportMessage>
{ {
private BeatmapDatabase beatmaps; private readonly BeatmapDatabase beatmaps;
public BeatmapIPCChannel(IIpcHost host, BeatmapDatabase beatmaps = null) public BeatmapIPCChannel(IIpcHost host, BeatmapDatabase beatmaps = null)
: base(host) : base(host)

View File

@ -10,7 +10,7 @@ namespace osu.Game.IPC
{ {
public class ScoreIPCChannel : IpcChannel<ScoreImportMessage> public class ScoreIPCChannel : IpcChannel<ScoreImportMessage>
{ {
private ScoreDatabase scores; private readonly ScoreDatabase scores;
public ScoreIPCChannel(IIpcHost host, ScoreDatabase scores = null) public ScoreIPCChannel(IIpcHost host, ScoreDatabase scores = null)
: base(host) : base(host)

Some files were not shown because too many files have changed in this diff Show More