1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-15 05:32:56 +08:00

Merge remote-tracking branch 'origin/master' into taiko_barlines

Conflicts:
	osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs
	osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs
	osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs
	osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj
This commit is contained in:
smoogipooo 2017-04-01 23:57:02 +09:00
commit b4ed8aeb98
267 changed files with 4349 additions and 1700 deletions

@ -1 +1 @@
Subproject commit 42ec8a62fb697f1d967a927549dc51336cd66968
Subproject commit bf6a3dc40176ee4f921012808070e014fc4a5779

@ -1 +1 @@
Subproject commit f85c594c182db2b01233e29ca52639b7baa00402
Subproject commit 12bbab717d372dadbd3220d38da862276ac97e98

View File

@ -53,7 +53,7 @@ namespace osu.Desktop.Deploy
private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.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;

View File

@ -13,10 +13,10 @@ using osu.Game.Modes.Taiko;
namespace osu.Desktop.Tests
{
[TestFixture]
public class BenchmarkTest
public class VisualTests
{
[Test]
public void TestBenchmark()
public void TestVisualTests()
{
using (var host = new HeadlessGameHost())
{
@ -25,7 +25,7 @@ namespace osu.Desktop.Tests
Ruleset.Register(new ManiaRuleset());
Ruleset.Register(new CatchRuleset());
host.Run(new Benchmark());
host.Run(new AutomatedVisualTestGame());
}
}
}

View File

@ -56,7 +56,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BenchmarkTest.cs" />
<Compile Include="VisualTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework.Desktop\osu.Framework.Desktop.csproj">

View File

@ -0,0 +1,20 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Testing;
using osu.Game;
namespace osu.Desktop.VisualTests
{
public class AutomatedVisualTestGame : OsuGameBase
{
protected override void LoadComplete()
{
base.LoadComplete();
// Have to construct this here, rather than in the constructor, because
// we depend on some dependencies to be loaded within OsuGameBase.load().
Add(new TestRunner(new TestBrowser()));
}
}
}

View File

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

View File

@ -1,45 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Screens.Testing;
using osu.Game;
namespace osu.Desktop.VisualTests
{
public class Benchmark : OsuGameBase
{
private double timePerTest = 200;
[BackgroundDependencyLoader]
private void load()
{
Host.MaximumDrawHz = int.MaxValue;
Host.MaximumUpdateHz = int.MaxValue;
Host.MaximumInactiveHz = int.MaxValue;
}
protected override void LoadComplete()
{
base.LoadComplete();
TestBrowser f = new TestBrowser();
Add(f);
Console.WriteLine($@"{Time}: Running {f.TestCount} tests for {timePerTest}ms each...");
for (int i = 1; i < f.TestCount; i++)
{
int loadableCase = i;
Scheduler.AddDelayed(delegate
{
f.LoadTest(loadableCase);
Console.WriteLine($@"{Time}: Switching to test #{loadableCase}");
}, loadableCase * timePerTest);
}
Scheduler.AddDelayed(Host.Exit, f.TestCount * timePerTest);
}
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Desktop.VisualTests
Ruleset.Register(new CatchRuleset());
if (benchmark)
host.Run(new Benchmark());
host.Run(new AutomatedVisualTestGame());
else
host.Run(new VisualTestGame());
}

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.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

@ -3,7 +3,7 @@
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Screens.Select.Options;
@ -26,7 +26,7 @@ namespace osu.Desktop.VisualTests.Tests
Add(overlay);
AddButton(@"Toggle", overlay.ToggleVisibility);
AddStep(@"Toggle", overlay.ToggleVisibility);
}
}
}

View File

@ -1,7 +1,7 @@
// 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.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Overlays;

View File

@ -1,7 +1,7 @@
// 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.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
@ -20,7 +20,7 @@ namespace osu.Desktop.VisualTests.Tests
Add(overlay = new DialogOverlay());
AddButton("dialog #1", () => overlay.Push(new PopupDialog
AddStep("dialog #1", () => overlay.Push(new PopupDialog
{
Icon = FontAwesome.fa_trash_o,
HeaderText = @"Confirm deletion of",
@ -40,7 +40,7 @@ namespace osu.Desktop.VisualTests.Tests
},
}));
AddButton("dialog #2", () => overlay.Push(new PopupDialog
AddStep("dialog #2", () => overlay.Push(new PopupDialog
{
Icon = FontAwesome.fa_gear,
HeaderText = @"What do you want to do with",

View File

@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams;
using osu.Game.Users;

View File

@ -5,7 +5,7 @@ using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Database;

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables;
@ -21,7 +21,7 @@ namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseHitObjects : TestCase
{
private FramedClock framedClock;
private readonly FramedClock framedClock;
private bool auto;
@ -34,7 +34,7 @@ namespace osu.Desktop.VisualTests.Tests
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 approachContainer;
@ -93,19 +93,28 @@ namespace osu.Desktop.VisualTests.Tests
playbackSpeed.TriggerChange();
AddButton(@"circles", () => load(HitObjectType.Circle));
AddButton(@"slider", () => load(HitObjectType.Slider));
AddButton(@"spinner", () => load(HitObjectType.Spinner));
AddStep(@"circles", () => load(HitObjectType.Circle));
AddStep(@"slider", () => load(HitObjectType.Slider));
AddStep(@"spinner", () => load(HitObjectType.Spinner));
AddToggle(@"auto", state => { auto = state; load(mode); });
AddToggleStep(@"auto", state => { auto = state; load(mode); });
ButtonsContainer.Add(new SpriteText { Text = "Playback Speed" });
ButtonsContainer.Add(new BasicSliderBar<double>
Add(new Container
{
Width = 150,
Height = 10,
SelectionColor = Color4.Orange,
Bindable = playbackSpeed
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new SpriteText { Text = "Playback Speed" },
new BasicSliderBar<double>
{
Width = 150,
Height = 10,
SelectionColor = Color4.Orange,
Bindable = playbackSpeed
}
}
});
framedClock.ProcessFrame();
@ -136,7 +145,7 @@ namespace osu.Desktop.VisualTests.Tests
if (auto)
{
h.State = ArmedState.Hit;
h.Judgement = new OsuJudgementInfo { Result = HitResult.Hit };
h.Judgement = new OsuJudgement { Result = HitResult.Hit };
}
playfieldContainer.Add(h);

View File

@ -1,16 +1,16 @@
// 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.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests
@ -38,19 +38,30 @@ namespace osu.Desktop.VisualTests.Tests
};
BindableInt bindable = new BindableInt { MinValue = 0, MaxValue = 200, Default = 50 };
bindable.ValueChanged += delegate { kc.FadeTime = bindable.Value; };
AddButton("Add Random", () =>
AddStep("Add Random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
kc.Add(new KeyCounterKeyboard(key));
});
ButtonsContainer.Add(new SpriteText { Text = "FadeTime" });
ButtonsContainer.Add(new TestSliderBar<int>
Add(new Container
{
Width = 150,
Height = 10,
SelectionColor = Color4.Orange,
Bindable = bindable
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new SpriteText { Text = "FadeTime" },
new TestSliderBar<int>
{
Width = 150,
Height = 10,
SelectionColor = Color4.Orange,
Bindable = bindable
}
}
});
Add(kc);
}
private class TestSliderBar<T> : SliderBar<T> where T : struct

View File

@ -3,10 +3,10 @@
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Screens.Testing;
using osu.Game.Modes;
using osu.Framework.Testing;
using osu.Game.Modes.Mods;
using osu.Game.Modes.Osu.Mods;
using osu.Game.Modes.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
@ -218,7 +218,7 @@ namespace osu.Desktop.VisualTests.Tests
Size = new Vector2(550f, 450f),
});
AddButton(@"New Scores", newScores);
AddStep(@"New Scores", newScores);
newScores();
}
}

View File

@ -1,7 +1,7 @@
// 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.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Sprites;
using osu.Game.Screens.Menu;

View File

@ -3,7 +3,7 @@
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Modes;
namespace osu.Desktop.VisualTests.Tests
@ -25,11 +25,11 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.BottomCentre,
});
AddButton("Toggle", modSelect.ToggleVisibility);
AddButton("osu!", () => modSelect.PlayMode.Value = PlayMode.Osu);
AddButton("osu!taiko", () => modSelect.PlayMode.Value = PlayMode.Taiko);
AddButton("osu!catch", () => modSelect.PlayMode.Value = PlayMode.Catch);
AddButton("osu!mania", () => modSelect.PlayMode.Value = PlayMode.Mania);
AddStep("Toggle", modSelect.ToggleVisibility);
AddStep("osu!", () => modSelect.PlayMode.Value = PlayMode.Osu);
AddStep("osu!taiko", () => modSelect.PlayMode.Value = PlayMode.Taiko);
AddStep("osu!catch", () => modSelect.PlayMode.Value = PlayMode.Catch);
AddStep("osu!mania", () => modSelect.PlayMode.Value = PlayMode.Mania);
}
}
}

View File

@ -1,7 +1,7 @@
// 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.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Overlays;
@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.Centre
};
Add(mc);
AddToggle(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
AddToggleStep(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
}
}
}

View File

@ -3,7 +3,7 @@
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.MathUtils;
using osu.Game.Overlays;
using System.Linq;
@ -30,13 +30,13 @@ namespace osu.Desktop.VisualTests.Tests
Origin = Anchor.TopRight,
});
AddToggle(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden);
AddToggleStep(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden);
AddButton(@"simple #1", sendNotification1);
AddButton(@"simple #2", sendNotification2);
AddButton(@"progress #1", sendProgress1);
AddButton(@"progress #2", sendProgress2);
AddButton(@"barrage", () => sendBarrage());
AddStep(@"simple #1", sendNotification1);
AddStep(@"simple #2", sendNotification2);
AddStep(@"progress #1", sendProgress1);
AddStep(@"progress #2", sendProgress2);
AddStep(@"barrage", () => sendBarrage());
}
private void sendBarrage(int remaining = 100)
@ -95,7 +95,7 @@ namespace osu.Desktop.VisualTests.Tests
progressingNotifications.Add(n);
}
private List<ProgressNotification> progressingNotifications = new List<ProgressNotification>();
private readonly List<ProgressNotification> progressingNotifications = new List<ProgressNotification>();
private void sendProgress1()
{

View File

@ -1,7 +1,7 @@
// 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.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests

View File

@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Logging;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests
@ -25,8 +25,8 @@ namespace osu.Desktop.VisualTests.Tests
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit")
});
AddButton("Pause", pauseOverlay.Show);
AddButton("Add Retry", delegate
AddStep("Pause", pauseOverlay.Show);
AddStep("Add Retry", delegate
{
retryCount++;
pauseOverlay.Retries = retryCount;

View File

@ -3,7 +3,7 @@
using System.Collections.Generic;
using osu.Desktop.VisualTests.Platform;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.MathUtils;
using osu.Game.Database;
using osu.Game.Modes;
@ -23,12 +23,10 @@ namespace osu.Desktop.VisualTests.Tests
public override void Reset()
{
base.Reset();
oldDb = Dependencies.Get<BeatmapDatabase>();
if (db == null)
{
storage = new TestStorage(@"TestCasePlaySongSelect");
db = new BeatmapDatabase(storage);
Dependencies.Cache(db, true);
var sets = new List<BeatmapSetInfo>();
@ -40,19 +38,16 @@ namespace osu.Desktop.VisualTests.Tests
Add(songSelect = new PlaySongSelect());
AddButton(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
AddButton(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
AddButton(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; });
AddButton(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; });
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
}
protected override void Dispose(bool isDisposing)
{
if (oldDb != null)
{
Dependencies.Cache(oldDb, true);
db = null;
}
base.Dispose(isDisposing);
}

View File

@ -4,7 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using OpenTK;
using osu.Framework.Graphics.Sprites;

View File

@ -6,7 +6,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Modes.UI;
@ -68,7 +68,7 @@ namespace osu.Desktop.VisualTests.Tests
};
Add(starsLabel);
AddButton(@"Reset all", delegate
AddStep(@"Reset all", delegate
{
score.Current.Value = 0;
comboCounter.Current.Value = 0;
@ -78,7 +78,7 @@ namespace osu.Desktop.VisualTests.Tests
starsLabel.Text = stars.Count.ToString("0.00");
});
AddButton(@"Hit! :D", delegate
AddStep(@"Hit! :D", delegate
{
score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0);
comboCounter.Increment();
@ -86,20 +86,20 @@ namespace osu.Desktop.VisualTests.Tests
accuracyCounter.SetFraction(numerator, denominator);
});
AddButton(@"miss...", delegate
AddStep(@"miss...", delegate
{
comboCounter.Current.Value = 0;
denominator++;
accuracyCounter.SetFraction(numerator, denominator);
});
AddButton(@"Alter stars", delegate
AddStep(@"Alter stars", delegate
{
stars.Count = RNG.NextSingle() * (stars.StarCount + 1);
starsLabel.Text = stars.Count.ToString("0.00");
});
AddButton(@"Stop counters", delegate
AddStep(@"Stop counters", delegate
{
score.StopRolling();
comboCounter.StopRolling();

View File

@ -3,7 +3,7 @@
using OpenTK;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Filter;

View File

@ -0,0 +1,135 @@
// 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.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseTaikoHitObjects : TestCase
{
public override string Description => "Taiko hit objects";
private bool kiai;
public override void Reset()
{
base.Reset();
AddToggleStep("Kiai", b =>
{
kiai = !kiai;
Reset();
});
Add(new CirclePiece
{
Position = new Vector2(100, 100),
Width = 0,
AccentColour = Color4.DarkRed,
KiaiMode = kiai,
Children = new[]
{
new CentreHitSymbolPiece()
}
});
Add(new StrongCirclePiece
{
Position = new Vector2(350, 100),
Width = 0,
AccentColour = Color4.DarkRed,
KiaiMode = kiai,
Children = new[]
{
new CentreHitSymbolPiece()
}
});
Add(new CirclePiece
{
Position = new Vector2(100, 300),
Width = 0,
AccentColour = Color4.DarkBlue,
KiaiMode = kiai,
Children = new[]
{
new RimHitSymbolPiece()
}
});
Add(new StrongCirclePiece
{
Position = new Vector2(350, 300),
Width = 0,
AccentColour = Color4.DarkBlue,
KiaiMode = kiai,
Children = new[]
{
new RimHitSymbolPiece()
}
});
Add(new CirclePiece
{
Position = new Vector2(100, 500),
Width = 0,
AccentColour = Color4.Orange,
KiaiMode = kiai,
Children = new[]
{
new SwellSymbolPiece()
}
});
Add(new DrumRollCircle(new CirclePiece
{
KiaiMode = kiai
})
{
Width = 250,
Position = new Vector2(575, 100)
});
Add(new DrumRollCircle(new StrongCirclePiece
{
KiaiMode = kiai
})
{
Width = 250,
Position = new Vector2(575, 300)
});
}
private class DrumRollCircle : BaseCircle
{
public DrumRollCircle(CirclePiece piece)
: base(piece)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Piece.AccentColour = colours.YellowDark;
}
}
private abstract class BaseCircle : Container
{
protected readonly CirclePiece Piece;
protected BaseCircle(CirclePiece piece)
{
Piece = piece;
Add(Piece);
}
}
}
}

View File

@ -1,8 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.MathUtils;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
@ -27,15 +30,24 @@ namespace osu.Desktop.VisualTests.Tests
{
base.Reset();
Clock.ProcessFrame();
AddButton("Hit!", addHitJudgement);
AddButton("Miss :(", addMissJudgement);
AddStep("Hit!", addHitJudgement);
AddStep("Miss :(", addMissJudgement);
AddStep("Swell", addSwell);
AddStep("Centre", () => addCentreHit(false));
AddStep("Strong Centre", () => addCentreHit(true));
AddButton("Add bar line", addBarLine);
AddStep("Rim", () => addRimHit(false));
AddStep("Strong Rim", () => addRimHit(true));
Add(playfield = new TaikoPlayfield
Add(new Container
{
Y = 200
RelativeSizeAxes = Axes.X,
Y = 200,
Padding = new MarginPadding { Left = 200 },
Children = new[]
{
playfield = new TaikoPlayfield()
}
});
}
@ -43,15 +55,14 @@ namespace osu.Desktop.VisualTests.Tests
{
TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great;
playfield.OnJudgement(new DrawableTestHit(new TaikoHitObject())
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 TaikoJudgementInfo
Judgement = new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = hitResult,
TimeOffset = 0,
ComboAtHit = 1,
SecondHit = RNG.Next(10) == 0
}
});
@ -59,13 +70,12 @@ namespace osu.Desktop.VisualTests.Tests
private void addMissJudgement()
{
playfield.OnJudgement(new DrawableTestHit(new TaikoHitObject())
playfield.OnJudgement(new DrawableTestHit(new Hit())
{
Judgement = new TaikoJudgementInfo
Judgement = new TaikoJudgement
{
Result = HitResult.Miss,
TimeOffset = 0,
ComboAtHit = 0
TimeOffset = 0
}
});
}
@ -83,14 +93,52 @@ namespace osu.Desktop.VisualTests.Tests
playfield.AddBarLine(isMajor ? new DrawableMajorBarLine(bl) : new DrawableBarLine(bl));
}
private class DrawableTestHit : DrawableHitObject<TaikoHitObject, TaikoJudgementInfo>
private void addSwell()
{
playfield.Add(new DrawableSwell(new Swell
{
StartTime = Time.Current + 1000,
EndTime = Time.Current + 5000,
PreEmpt = 1000
}));
}
private void addCentreHit(bool strong)
{
Hit h = new Hit
{
StartTime = Time.Current + 1000,
PreEmpt = 1000
};
if (strong)
playfield.Add(new DrawableStrongCentreHit(h));
else
playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
{
Hit h = new Hit
{
StartTime = Time.Current + 1000,
PreEmpt = 1000
};
if (strong)
playfield.Add(new DrawableStrongRimHit(h));
else
playfield.Add(new DrawableRimHit(h));
}
private class DrawableTestHit : DrawableHitObject<TaikoHitObject, TaikoJudgement>
{
public DrawableTestHit(TaikoHitObject hitObject)
: base(hitObject)
{
}
protected override TaikoJudgementInfo CreateJudgementInfo() => new TaikoJudgementInfo();
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{

View File

@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;

View File

@ -1,7 +1,7 @@
// 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.Screens.Testing;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play;

View File

@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Platform;
using osu.Framework.Screens.Testing;
using osu.Framework.Testing;
using osu.Game;
using osu.Game.Screens.Backgrounds;
@ -24,6 +24,11 @@ namespace osu.Desktop.VisualTests
public override void SetHost(GameHost host)
{
base.SetHost(host);
host.UpdateThread.InactiveHz = host.UpdateThread.ActiveHz;
host.DrawThread.InactiveHz = host.DrawThread.ActiveHz;
host.InputThread.InactiveHz = host.InputThread.ActiveHz;
host.Window.CursorState = CursorState.Hidden;
}
}

View File

@ -150,6 +150,10 @@
<Project>{65dc628f-a640-4111-ab35-3a5652bc1e17}</Project>
<Name>osu.Framework.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\osu-framework\osu.Framework.Testing\osu.Framework.Testing.csproj">
<Project>{007b2356-ab6f-4bd9-96d5-116fc2dce69a}</Project>
<Name>osu.Framework.Testing</Name>
</ProjectReference>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
<Name>osu.Framework</Name>
@ -180,7 +184,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Benchmark.cs" />
<Compile Include="AutomatedVisualTestGame.cs" />
<Compile Include="Program.cs" />
<Compile Include="Tests\TestCaseChatDisplay.cs" />
<Compile Include="Tests\TestCaseDrawings.cs" />
@ -194,6 +198,7 @@
<Compile Include="Tests\TestCaseReplay.cs" />
<Compile Include="Tests\TestCaseScoreCounter.cs" />
<Compile Include="Tests\TestCaseTabControl.cs" />
<Compile Include="Tests\TestCaseTaikoHitObjects.cs" />
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
<Compile Include="Tests\TestCaseTextAwesome.cs" />
<Compile Include="Tests\TestCasePlaySongSelect.cs" />
@ -207,12 +212,11 @@
<Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" />
<Compile Include="Tests\TestCaseLeaderboard.cs" />
<Compile Include="Beatmaps\TestWorkingBeatmap.cs" />
<Compile Include="Tests\TestCaseBeatmapDetailArea.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup />
<ItemGroup>
<Folder Include="Beatmaps\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -0,0 +1,29 @@
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>

View File

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

View File

@ -123,7 +123,10 @@
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4" />
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
@ -150,6 +153,7 @@
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="OpenTK.dll.config" />
<None Include="osu!.res" />
<None Include="packages.config" />
<None Include="Properties\app.manifest" />

View File

@ -7,6 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" />
<package id="Splat" version="2.0.0" targetFramework="net45" />
<package id="squirrel.windows" version="1.5.2" targetFramework="net45" />
</packages>

View File

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

View File

@ -5,7 +5,10 @@ using osu.Game.Modes.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
{
private CatchBaseHit h;
private readonly CatchBaseHit h;
public DrawableFruit(CatchBaseHit h)
{
@ -29,7 +29,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{
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 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.Objects;
using osu.Game.Modes.Scoring;
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(HitRenderer<CatchBaseHit, CatchJudgementInfo> hitRenderer)
public CatchScoreProcessor(HitRenderer<CatchBaseHit, CatchJudgement> hitRenderer)
: base(hitRenderer)
{
}
protected override void UpdateCalculations(CatchJudgementInfo newJudgement)
protected override void OnNewJudgement(CatchJudgement judgement)
{
}
}

View File

@ -5,12 +5,14 @@ using osu.Game.Beatmaps;
using osu.Game.Modes.Catch.Beatmaps;
using osu.Game.Modes.Catch.Judgements;
using osu.Game.Modes.Catch.Objects;
using osu.Game.Modes.Catch.Scoring;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
namespace osu.Game.Modes.Catch.UI
{
public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgementInfo>
public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgement>
{
public CatchHitRenderer(WorkingBeatmap beatmap)
: base(beatmap)
@ -23,8 +25,8 @@ namespace osu.Game.Modes.Catch.UI
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
{
public class CatchPlayfield : Playfield<CatchBaseHit, CatchJudgementInfo>
public class CatchPlayfield : Playfield<CatchBaseHit, CatchJudgement>
{
public CatchPlayfield()
{

View File

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

View File

@ -5,7 +5,10 @@ using osu.Game.Modes.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.Screens.Play;
using System.Collections.Generic;
using osu.Game.Modes.Mania.Scoring;
using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Mania
{

View File

@ -26,7 +26,7 @@ namespace osu.Game.Modes.Mania.Objects.Drawable
{
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 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.Objects;
using osu.Game.Modes.Scoring;
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(HitRenderer<ManiaBaseHit, ManiaJudgementInfo> hitRenderer)
public ManiaScoreProcessor(HitRenderer<ManiaBaseHit, ManiaJudgement> hitRenderer)
: base(hitRenderer)
{
}
protected override void UpdateCalculations(ManiaJudgementInfo newJudgement)
protected override void OnNewJudgement(ManiaJudgement judgement)
{
}
}

View File

@ -5,12 +5,14 @@ using osu.Game.Beatmaps;
using osu.Game.Modes.Mania.Beatmaps;
using osu.Game.Modes.Mania.Judgements;
using osu.Game.Modes.Mania.Objects;
using osu.Game.Modes.Mania.Scoring;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
namespace osu.Game.Modes.Mania.UI
{
public class ManiaHitRenderer : HitRenderer<ManiaBaseHit, ManiaJudgementInfo>
public class ManiaHitRenderer : HitRenderer<ManiaBaseHit, ManiaJudgement>
{
private readonly int columns;
@ -26,8 +28,8 @@ namespace osu.Game.Modes.Mania.UI
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
{
public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgementInfo>
public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgement>
{
public ManiaPlayfield(int columns)
{

View File

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

View File

@ -44,11 +44,8 @@ namespace osu.Game.Modes.Osu.Beatmaps
{
StartTime = original.StartTime,
Sample = original.Sample,
CurveObject = curveData,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false
};
}
@ -60,7 +57,6 @@ namespace osu.Game.Modes.Osu.Beatmaps
StartTime = original.StartTime,
Sample = original.Sample,
Position = new Vector2(512, 384) / 2,
EndTime = endTimeData.EndTime
};
}
@ -69,9 +65,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
{
StartTime = original.StartTime,
Sample = original.Sample,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false
};
}
@ -81,7 +75,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
if (endIndex == -1)
endIndex = hitObjects.Count - 1;
int stackDistance = 3;
const int stack_distance = 3;
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * stackLeniency;
// 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.
break;
if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stackDistance ||
stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stackDistance)
if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
@ -174,14 +168,14 @@ namespace osu.Game.Modes.Osu.Beatmaps
* o <- hitCircle has stack of -1
* 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;
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).
OsuHitObject objectJ = hitObjects[j];
if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stackDistance)
if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
objectJ.StackHeight -= offset;
}
@ -190,7 +184,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
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.
//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.
break;
if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stackDistance)
if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
objectN.StackHeight = objectI.StackHeight + 1;
objectI = objectN;

View File

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

View File

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

View File

@ -7,7 +7,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
namespace osu.Game.Modes.Osu.Objects.Drawables.Connections
{

View File

@ -3,7 +3,6 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
using OpenTK;
@ -13,37 +12,33 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{
private OsuHitObject osuObject;
public ApproachCircle ApproachCircle;
private CirclePiece circle;
private RingPiece ring;
private FlashPiece flash;
private ExplodePiece explode;
private NumberPiece number;
private GlowPiece glow;
private readonly CirclePiece circle;
private readonly RingPiece ring;
private readonly FlashPiece flash;
private readonly ExplodePiece explode;
private readonly NumberPiece number;
private readonly GlowPiece glow;
public DrawableHitCircle(OsuHitObject h) : base(h)
{
Origin = Anchor.Centre;
osuObject = h;
Position = osuObject.StackedPosition;
Scale = new Vector2(osuObject.Scale);
Position = HitObject.StackedPosition;
Scale = new Vector2(HitObject.Scale);
Children = new Drawable[]
{
glow = new GlowPiece
{
Colour = osuObject.ComboColour
Colour = AccentColour
},
circle = new CirclePiece
{
Colour = osuObject.ComboColour,
Colour = AccentColour,
Hit = () =>
{
if (Judgement.Result.HasValue) return false;
if (Judgement.Result != HitResult.None) return false;
Judgement.PositionOffset = Vector2.Zero; //todo: set to correct value
UpdateJudgement(true);
@ -58,11 +53,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
flash = new FlashPiece(),
explode = new ExplodePiece
{
Colour = osuObject.ComboColour,
Colour = AccentColour,
},
ApproachCircle = new ApproachCircle
{
Colour = osuObject.ComboColour,
Colour = AccentColour,
}
};
@ -115,8 +110,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ApproachCircle.FadeOut();
double endTime = (osuObject as IHasEndTime)?.EndTime ?? osuObject.StartTime;
double duration = endTime - osuObject.StartTime;
double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
double duration = endTime - HitObject.StartTime;
glow.Delay(duration);
glow.FadeOut(400);

View File

@ -7,18 +7,19 @@ using osu.Game.Modes.Osu.Judgements;
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_FADEIN = 400;
public const float TIME_FADEOUT = 500;
public DrawableOsuHitObject(OsuHitObject hitObject)
protected DrawableOsuHitObject(OsuHitObject 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)
{

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;
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
{
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 SliderBall ball;
private readonly SliderBody body;
private readonly SliderBall ball;
private SliderBouncer bouncer2;
private readonly SliderBouncer bouncer2;
public DrawableSlider(Slider s) : base(s)
{
@ -39,6 +39,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
body = new SliderBody(s)
{
AccentColour = AccentColour,
Position = s.StackedPosition,
PathWidth = s.Scale * 64,
},
@ -56,6 +57,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ball = new SliderBall(s)
{
Scale = new Vector2(s.Scale),
AccentColour = AccentColour
},
initialCircle = new DrawableHitCircle(new HitCircle
{

View File

@ -7,7 +7,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
@ -18,7 +17,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableSliderTick : DrawableOsuHitObject
{
private SliderTick sliderTick;
private readonly SliderTick sliderTick;
public double FadeInTime;
public double FadeOutTime;
@ -27,7 +26,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
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)
{
@ -48,7 +47,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = sliderTick.ComboColour,
Colour = AccentColour,
Alpha = 0.3f,
}
};

View File

@ -4,7 +4,6 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
@ -15,12 +14,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableSpinner : DrawableOsuHitObject
{
private Spinner spinner;
private readonly Spinner spinner;
private SpinnerDisc disc;
private SpinnerBackground background;
private Container circleContainer;
private DrawableHitCircle circle;
private readonly SpinnerDisc disc;
private readonly SpinnerBackground background;
private readonly Container circleContainer;
private readonly DrawableHitCircle circle;
public DrawableSpinner(Spinner s) : base(s)
{
@ -48,7 +47,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
DiscColour = s.ComboColour
DiscColour = AccentColour
},
circleContainer = new Container
{
@ -108,9 +107,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
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);

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
{
private Sprite approachCircle;
private readonly Sprite approachCircle;
public ApproachCircle()
{

View File

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

View File

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

View File

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

View File

@ -4,7 +4,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using OpenTK.Graphics;
@ -12,11 +11,27 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class SliderBall : CircularContainer, ISliderProgress
{
private readonly Slider slider;
private Box follow;
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)
{
this.slider = slider;
@ -49,9 +64,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
Alpha = 1,
Children = new[]
{
new Box
ball = new Box
{
Colour = slider.ComboColour,
Colour = AccentColour,
Alpha = 0.4f,
Width = width,
Height = width,

View File

@ -13,27 +13,45 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Configuration;
using OpenTK;
using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class SliderBody : Container, ISliderProgress
{
private Path path;
private BufferedContainer container;
private readonly Path path;
private readonly BufferedContainer container;
public float PathWidth
{
get { return path.PathWidth; }
set
{
path.PathWidth = value;
}
set { path.PathWidth = value; }
}
public double? SnakedStart { 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)
{
slider = s;
@ -82,7 +100,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
snakingIn = config.GetBindable<bool>(OsuConfig.SnakingInSliders);
snakingOut = config.GetBindable<bool>(OsuConfig.SnakingOutSliders);
int textureWidth = (int)PathWidth * 2;
reloadTexture();
}
private void reloadTexture()
{
var texture = new Texture(textureWidth, 1);
//initialise background
var upload = new TextureUpload(textureWidth * 4);
@ -110,19 +133,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
progress -= border_portion;
bytes[i * 4] = (byte)(slider.ComboColour.R * 255);
bytes[i * 4 + 1] = (byte)(slider.ComboColour.G * 255);
bytes[i * 4 + 2] = (byte)(slider.ComboColour.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] = (byte)(AccentColour.R * 255);
bytes[i * 4 + 1] = (byte)(AccentColour.G * 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) * (AccentColour.A * 255));
}
}
var texture = new Texture(textureWidth, 1);
texture.SetData(upload);
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)
{
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 bool isEnd;
private TextAwesome icon;
private readonly TextAwesome icon;
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 Color4 ComboColour { get; set; }
public Color4 ComboColour { get; set; } = Color4.Gray;
public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; }

View File

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

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Modes.Osu.Objects;
@ -10,17 +9,19 @@ using osu.Game.Modes.Osu.Objects.Drawables;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Graphics;
using osu.Game.Modes.Objects.Types;
using osu.Game.Modes.Replays;
namespace osu.Game.Modes.Osu
{
public class OsuAutoReplay : LegacyReplay
public class OsuAutoReplay : Replay
{
private static readonly Vector2 spinner_centre = new Vector2(256, 192);
private const float spin_radius = 50;
private Beatmap<OsuHitObject> beatmap;
private readonly Beatmap<OsuHitObject> beatmap;
public OsuAutoReplay(Beatmap<OsuHitObject> beatmap)
{
@ -29,19 +30,22 @@ namespace osu.Game.Modes.Osu
createAutoReplay();
}
internal class LegacyReplayFrameComparer : IComparer<LegacyReplayFrame>
private class ReplayFrameComparer : IComparer<ReplayFrame>
{
public int Compare(LegacyReplayFrame f1, LegacyReplayFrame f2)
public int Compare(ReplayFrame f1, ReplayFrame f2)
{
if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null");
if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null");
return f1.Time.CompareTo(f2.Time);
}
}
private static IComparer<LegacyReplayFrame> replayFrameComparer = new LegacyReplayFrameComparer();
private static readonly IComparer<ReplayFrame> replay_frame_comparer = new ReplayFrameComparer();
private int findInsertionIndex(LegacyReplayFrame frame)
private int findInsertionIndex(ReplayFrame frame)
{
int index = Frames.BinarySearch(frame, replayFrameComparer);
int index = Frames.BinarySearch(frame, replay_frame_comparer);
if (index < 0)
{
@ -59,7 +63,7 @@ namespace osu.Game.Modes.Osu
return index;
}
private void addFrameToReplay(LegacyReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
private void addFrameToReplay(ReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
private static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
@ -74,9 +78,9 @@ namespace osu.Game.Modes.Osu
EasingTypes preferredEasing = DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
addFrameToReplay(new LegacyReplayFrame(-100000, 256, 500, LegacyButtonState.None));
addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, LegacyButtonState.None));
addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, LegacyButtonState.None));
addFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None));
addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None));
addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None));
// We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
float frameDelay = (float)applyModsToRate(1000.0 / 60.0);
@ -106,18 +110,18 @@ namespace osu.Game.Modes.Osu
//Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, LegacyButtonState.None));
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, LegacyButtonState.None));
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, LegacyButtonState.None));
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
}
@ -173,13 +177,13 @@ namespace osu.Game.Modes.Osu
// Do some nice easing for cursor movements
if (Frames.Count > 0)
{
LegacyReplayFrame lastFrame = Frames[Frames.Count - 1];
ReplayFrame lastFrame = Frames[Frames.Count - 1];
// Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime);
if (waitTime > lastFrame.Time)
{
lastFrame = new LegacyReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
addFrameToReplay(lastFrame);
}
@ -196,7 +200,7 @@ namespace osu.Game.Modes.Osu
for (double time = lastFrame.Time + frameDelay; time < h.StartTime; time += frameDelay)
{
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPosition, lastFrame.Time, h.StartTime, easing);
addFrameToReplay(new LegacyReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
addFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
}
buttonIndex = 0;
@ -207,12 +211,12 @@ namespace osu.Game.Modes.Osu
}
}
LegacyButtonState button = buttonIndex % 2 == 0 ? LegacyButtonState.Left1 : LegacyButtonState.Right1;
ReplayButtonState button = buttonIndex % 2 == 0 ? ReplayButtonState.Left1 : ReplayButtonState.Right1;
double hEndTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
LegacyReplayFrame newFrame = new LegacyReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
LegacyReplayFrame endFrame = new LegacyReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None);
ReplayFrame newFrame = new ReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None);
// Decrement because we want the previous frame, not the next one
int index = findInsertionIndex(newFrame) - 1;
@ -220,19 +224,19 @@ namespace osu.Game.Modes.Osu
// Do we have a previous frame? No need to check for < replay.Count since we decremented!
if (index >= 0)
{
LegacyReplayFrame previousFrame = Frames[index];
ReplayFrame previousFrame = Frames[index];
var previousButton = previousFrame.ButtonState;
// If a button is already held, then we simply alternate
if (previousButton != LegacyButtonState.None)
if (previousButton != ReplayButtonState.None)
{
Debug.Assert(previousButton != (LegacyButtonState.Left1 | LegacyButtonState.Right1));
Debug.Assert(previousButton != (ReplayButtonState.Left1 | ReplayButtonState.Right1));
// Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button.
if (previousButton == button)
{
button = (LegacyButtonState.Left1 | LegacyButtonState.Right1) & ~button;
newFrame.SetButtonStates(button);
button = (ReplayButtonState.Left1 | ReplayButtonState.Right1) & ~button;
newFrame.ButtonState = button;
}
// We always follow the most recent slider / spinner, so remove any other frames that occur while it exists.
@ -246,7 +250,7 @@ namespace osu.Game.Modes.Osu
{
// Don't affect frames which stop pressing a button!
if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton)
Frames[j].SetButtonStates(button);
Frames[j].ButtonState = button;
}
}
}
@ -270,13 +274,13 @@ namespace osu.Game.Modes.Osu
t = applyModsToTime(j - h.StartTime) * spinnerDirection;
Vector2 pos = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
addFrameToReplay(new LegacyReplayFrame((int)j, pos.X, pos.Y, button));
addFrameToReplay(new ReplayFrame((int)j, pos.X, pos.Y, button));
}
t = applyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
addFrameToReplay(new LegacyReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
addFrameToReplay(new ReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
endFrame.MouseX = endPosition.X;
endFrame.MouseY = endPosition.Y;
@ -288,10 +292,10 @@ namespace osu.Game.Modes.Osu
for (double j = frameDelay; j < s.Duration; j += frameDelay)
{
Vector2 pos = s.PositionAt(j / s.Duration);
addFrameToReplay(new LegacyReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
addFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
}
addFrameToReplay(new LegacyReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
addFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!

View File

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

View File

@ -1,7 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Modes.Osu
using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Osu.Scoring
{
internal class OsuScore : Score
{

View File

@ -4,17 +4,18 @@
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Scoring;
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(HitRenderer<OsuHitObject, OsuJudgementInfo> hitRenderer)
public OsuScoreProcessor(HitRenderer<OsuHitObject, OsuJudgement> hitRenderer)
: base(hitRenderer)
{
}
@ -27,7 +28,7 @@ namespace osu.Game.Modes.Osu
Accuracy.Value = 1;
}
protected override void UpdateCalculations(OsuJudgementInfo judgement)
protected override void OnNewJudgement(OsuJudgement judgement)
{
if (judgement != null)
{
@ -47,9 +48,8 @@ namespace osu.Game.Modes.Osu
int score = 0;
int maxScore = 0;
foreach (var judgementInfo in Judgements)
foreach (var j in Judgements)
{
var j = judgementInfo;
score += j.ScoreValue;
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.Objects;
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.Screens.Play;
namespace osu.Game.Modes.Osu.UI
{
public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgementInfo>
public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgement>
{
public OsuHitRenderer(WorkingBeatmap beatmap)
: base(beatmap)
@ -25,11 +27,11 @@ namespace osu.Game.Modes.Osu.UI
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 DrawableHitObject<OsuHitObject, OsuJudgementInfo> GetVisualRepresentation(OsuHitObject h)
protected override DrawableHitObject<OsuHitObject, OsuJudgement> GetVisualRepresentation(OsuHitObject h)
{
var circle = h as HitCircle;
if (circle != null)

View File

@ -15,11 +15,11 @@ using osu.Game.Modes.Osu.Judgements;
namespace osu.Game.Modes.Osu.UI
{
public class OsuPlayfield : Playfield<OsuHitObject, OsuJudgementInfo>
public class OsuPlayfield : Playfield<OsuHitObject, OsuJudgement>
{
private Container approachCircles;
private Container judgementLayer;
private ConnectionRenderer<OsuHitObject> connectionLayer;
private readonly Container approachCircles;
private readonly Container judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
public override Vector2 Size
{
@ -65,7 +65,7 @@ namespace osu.Game.Modes.Osu.UI
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;
@ -83,9 +83,13 @@ namespace osu.Game.Modes.Osu.UI
.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);
}

View File

@ -48,7 +48,7 @@
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
<Compile Include="Objects\Drawables\Connections\ConnectionRenderer.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\SpinnerBackground.cs" />
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
@ -58,7 +58,7 @@
<Compile Include="Objects\Drawables\Pieces\ExplodePiece.cs" />
<Compile Include="Objects\Drawables\Pieces\FlashPiece.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\DrawableSliderTick.cs" />
<Compile Include="Objects\Drawables\Pieces\RingPiece.cs" />
@ -72,8 +72,8 @@
<Compile Include="OsuAutoReplay.cs" />
<Compile Include="OsuDifficultyCalculator.cs" />
<Compile Include="OsuKeyConversionInputManager.cs" />
<Compile Include="OsuScore.cs" />
<Compile Include="OsuScoreProcessor.cs" />
<Compile Include="Scoring\OsuScore.cs" />
<Compile Include="Scoring\OsuScoreProcessor.cs" />
<Compile Include="UI\OsuHitRenderer.cs" />
<Compile Include="UI\OsuPlayfield.cs" />
<Compile Include="OsuRuleset.cs" />

View File

@ -2,18 +2,95 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
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 System.Collections.Generic;
using System.Linq;
namespace osu.Game.Modes.Taiko.Beatmaps
{
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)
{
if (original is LegacyBeatmap)
original.TimingInfo.ControlPoints.ForEach(c => c.VelocityAdjustment /= legacy_velocity_scale);
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;
// Old osu! used hit sounding to determine various hit type information
SampleType sample = original.Sample?.Type ?? SampleType.None;
bool strong = (sample & SampleType.Finish) > 0;
if (distanceData != null)
{
return new DrumRoll
{
StartTime = original.StartTime,
Sample = original.Sample,
IsStrong = strong,
Distance = distanceData.Distance * (repeatsData?.RepeatCount ?? 1)
};
}
if (endTimeData != null)
{
// We compute the end time manually to add in the Bash convert factor
return new Swell
{
StartTime = original.StartTime,
Sample = original.Sample,
IsStrong = strong,
EndTime = original.StartTime + endTimeData.Duration * bash_convert_factor
};
}
bool isCentre = (sample & ~(SampleType.Finish | SampleType.Normal)) == 0;
if (isCentre)
{
return new CentreHit
{
StartTime = original.StartTime,
Sample = original.Sample,
IsStrong = strong
};
}
return new RimHit
{
StartTime = original.StartTime,
Sample = original.Sample,
IsStrong = strong,
};
}
}

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

View File

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

View File

@ -0,0 +1,25 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Taiko.Judgements
{
public class TaikoStrongHitJudgement : TaikoJudgement, IPartialJudgement
{
public bool Changed { get; set; }
public override bool SecondHit
{
get { return base.SecondHit; }
set
{
if (base.SecondHit == value)
return;
base.SecondHit = value;
Changed = true;
}
}
}
}

View File

@ -1,7 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Modes.Mods;
using osu.Game.Modes.Scoring;
using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.Taiko.Replays;
using osu.Game.Users;
namespace osu.Game.Modes.Taiko.Mods
{
@ -61,4 +66,13 @@ namespace osu.Game.Modes.Taiko.Mods
{
}
public class TaikoModAutoplay : ModAutoplay<TaikoHitObject>
{
protected override Score CreateReplayScore(Beatmap<TaikoHitObject> beatmap) => new Score
{
User = new User { Username = "mekkadosu!" },
Replay = new TaikoAutoReplay(beatmap)
};
}
}

View File

@ -0,0 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Modes.Taiko.Objects
{
public class CentreHit : Hit
{
}
}

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 OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
using osu.Game.Graphics;
using osu.Framework.Allocation;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableCentreHit : DrawableHit
{
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
private readonly CirclePiece circlePiece;
public DrawableCentreHit(Hit hit)
: base(hit)
{
Add(circlePiece = new CirclePiece
{
Children = new[]
{
new CentreHitSymbolPiece()
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.PinkDarker;
}
}
}

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.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using System.Linq;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableDrumRoll : DrawableTaikoHitObject
{
private readonly DrumRoll drumRoll;
public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
this.drumRoll = drumRoll;
int tickIndex = 0;
foreach (var tick in drumRoll.Ticks)
{
var newTick = new DrawableDrumRollTick(tick)
{
Depth = tickIndex,
X = (float)((tick.StartTime - HitObject.StartTime) / drumRoll.Duration)
};
AddNested(newTick);
tickIndex++;
}
}
protected override void UpdateState(ArmedState state)
{
}
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
return;
if (Judgement.TimeOffset < 0)
return;
int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit);
if (countHit > drumRoll.RequiredGoodHits)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = countHit >= drumRoll.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Game.Modes.Taiko.Judgements;
using System;
using osu.Game.Modes.Objects.Drawables;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableDrumRollTick : DrawableTaikoHitObject
{
private readonly DrumRollTick tick;
public DrawableDrumRollTick(DrumRollTick tick)
: base(tick)
{
this.tick = tick;
}
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement();
protected override void CheckJudgement(bool userTriggered)
{
if (!userTriggered)
{
if (Judgement.TimeOffset > tick.HitWindow)
Judgement.Result = HitResult.Miss;
return;
}
if (Math.Abs(Judgement.TimeOffset) < tick.HitWindow)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great;
}
}
protected override void UpdateState(ArmedState state)
{
}
protected override void UpdateScrollPosition(double time)
{
// Drum roll ticks shouldn't move
}
protected override bool HandleKeyPress(Key key)
{
return Judgement.Result == HitResult.None && UpdateJudgement(true);
}
}
}

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 OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using System;
using System.Linq;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public abstract class DrawableHit : DrawableTaikoHitObject
{
/// <summary>
/// A list of keys which can result in hits for this HitObject.
/// </summary>
protected abstract Key[] HitKeys { get; }
protected override Container<Framework.Graphics.Drawable> Content => bodyContainer;
private readonly Hit hit;
/// <summary>
/// Whether the last key pressed is a valid hit key.
/// </summary>
private bool validKeyPressed;
private readonly Container bodyContainer;
protected DrawableHit(Hit hit)
: base(hit)
{
this.hit = hit;
AddInternal(bodyContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
protected override void CheckJudgement(bool userTriggered)
{
if (!userTriggered)
{
if (Judgement.TimeOffset > hit.HitWindowGood)
Judgement.Result = HitResult.Miss;
return;
}
double hitOffset = Math.Abs(Judgement.TimeOffset);
if (hitOffset > hit.HitWindowMiss)
return;
if (!validKeyPressed)
Judgement.Result = HitResult.Miss;
else if (hitOffset < hit.HitWindowGood)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = hitOffset < hit.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
protected override bool HandleKeyPress(Key key)
{
if (Judgement.Result != HitResult.None)
return false;
validKeyPressed = HitKeys.Contains(key);
return UpdateJudgement(true);
}
protected override void UpdateState(ArmedState state)
{
Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true);
switch (State)
{
case ArmedState.Idle:
Delay(hit.HitWindowMiss);
break;
case ArmedState.Miss:
FadeOut(100);
break;
case ArmedState.Hit:
bodyContainer.ScaleTo(0.8f, 400, EasingTypes.OutQuad);
bodyContainer.MoveToY(-200, 250, EasingTypes.Out);
bodyContainer.Delay(250);
bodyContainer.MoveToY(0, 500, EasingTypes.In);
FadeOut(600);
break;
}
Expire();
}
}
}

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.Framework.Allocation;
using osu.Game.Graphics;
using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableRimHit : DrawableHit
{
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
private readonly CirclePiece circlePiece;
public DrawableRimHit(Hit hit)
: base(hit)
{
Add(circlePiece = new CirclePiece
{
Children = new[]
{
new RimHitSymbolPiece()
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.BlueDarker;
}
}
}

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 OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableStrongCentreHit : DrawableStrongHit
{
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
private readonly CirclePiece circlePiece;
public DrawableStrongCentreHit(Hit hit)
: base(hit)
{
Add(circlePiece = new StrongCirclePiece
{
Children = new []
{
new CentreHitSymbolPiece()
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.PinkDarker;
}
}
}

View File

@ -0,0 +1,89 @@
// 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.Input;
using System;
using System.Linq;
using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public abstract class DrawableStrongHit : DrawableHit
{
/// <summary>
/// The lenience for the second key press.
/// This does not adjust by map difficulty in ScoreV2 yet.
/// </summary>
private const double second_hit_window = 30;
private double firstHitTime;
private bool firstKeyHeld;
private Key firstHitKey;
protected DrawableStrongHit(Hit hit)
: base(hit)
{
}
protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
protected override void CheckJudgement(bool userTriggered)
{
if (Judgement.Result == HitResult.None)
{
base.CheckJudgement(userTriggered);
return;
}
if (!userTriggered)
return;
// If we get here, we're assured that the key pressed is the correct secondary key
if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
Judgement.SecondHit = true;
}
protected override bool HandleKeyPress(Key key)
{
// Check if we've handled the first key
if (Judgement.Result == HitResult.None)
{
// First key hasn't been handled yet, attempt to handle it
bool handled = base.HandleKeyPress(key);
if (handled)
{
firstHitTime = Time.Current;
firstHitKey = key;
}
return handled;
}
// If we've already hit the second key, don't handle this object any further
if (Judgement.SecondHit)
return false;
// Don't handle represses of the first key
if (firstHitKey == key)
return false;
// Don't handle invalid hit key presses
if (!HitKeys.Contains(key))
return false;
// Assume the intention was to hit the strong hit with both keys only if the first key is still being held down
return firstKeyHeld && UpdateJudgement(true);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
firstKeyHeld = state.Keyboard.Keys.Contains(firstHitKey);
return base.OnKeyDown(state, args);
}
}
}

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.Framework.Allocation;
using osu.Game.Graphics;
using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableStrongRimHit : DrawableStrongHit
{
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
private readonly CirclePiece circlePiece;
public DrawableStrongRimHit(Hit hit)
: base(hit)
{
Add(circlePiece = new StrongCirclePiece
{
Children = new[]
{
new RimHitSymbolPiece()
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.BlueDarker;
}
}
}

View File

@ -0,0 +1,242 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
using System;
using System.Linq;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableSwell : DrawableTaikoHitObject
{
/// <summary>
/// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime.
/// This is only ever invoked once.
/// </summary>
public event Action OnStart;
private const float target_ring_thick_border = 1.4f;
private const float target_ring_thin_border = 1f;
private const float target_ring_scale = 5f;
private const float inner_ring_alpha = 0.65f;
private readonly Swell swell;
private readonly Container bodyContainer;
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
private readonly CirclePiece circlePiece;
private readonly Key[] rimKeys = { Key.D, Key.K };
private readonly Key[] centreKeys = { Key.F, Key.J };
private Key[] lastKeySet;
/// <summary>
/// The amount of times the user has hit this swell.
/// </summary>
private int userHits;
private bool hasStarted;
private readonly SwellSymbolPiece symbol;
public DrawableSwell(Swell swell)
: base(swell)
{
this.swell = swell;
Children = new Framework.Graphics.Drawable[]
{
bodyContainer = new Container
{
Children = new Framework.Graphics.Drawable[]
{
expandingRing = new CircularContainer
{
Name = "Expanding ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
BlendingMode = BlendingMode.Additive,
Masking = true,
Children = new []
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = inner_ring_alpha,
}
}
},
targetRing = new CircularContainer
{
Name = "Target ring (thick border)",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
Masking = true,
BorderThickness = target_ring_thick_border,
BlendingMode = BlendingMode.Additive,
Children = new Framework.Graphics.Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
},
new CircularContainer
{
Name = "Target ring (thin border)",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = target_ring_thin_border,
BorderColour = Color4.White,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
},
circlePiece = new CirclePiece
{
Children = new []
{
symbol = new SwellSymbolPiece()
}
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.YellowDark;
expandingRing.Colour = colours.YellowLight;
targetRing.BorderColour = colours.YellowDark.Opacity(0.25f);
}
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
{
userHits++;
var completion = (float)userHits / swell.RequiredHits;
expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
expandingRing.Delay(50);
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
expandingRing.DelayReset();
symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, EasingTypes.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint);
if (userHits == swell.RequiredHits)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great;
}
}
else
{
if (Judgement.TimeOffset < 0)
return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
if (userHits > swell.RequiredHits / 2)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
}
protected override void UpdateState(ArmedState state)
{
const float preempt = 100;
Delay(HitObject.StartTime - Time.Current - preempt, true);
targetRing.ScaleTo(target_ring_scale, preempt * 4, EasingTypes.OutQuint);
Delay(preempt, true);
Delay(Judgement.TimeOffset + swell.Duration, true);
const float out_transition_time = 300;
switch (state)
{
case ArmedState.Hit:
bodyContainer.ScaleTo(1.4f, out_transition_time);
break;
}
FadeOut(out_transition_time, EasingTypes.Out);
Expire();
}
protected override void UpdateScrollPosition(double time)
{
// Make the swell stop at the hit target
double t = Math.Min(HitObject.StartTime, time);
if (t == HitObject.StartTime && !hasStarted)
{
OnStart?.Invoke();
hasStarted = true;
}
base.UpdateScrollPosition(t);
}
protected override bool HandleKeyPress(Key key)
{
if (Judgement.Result != HitResult.None)
return false;
// Don't handle keys before the swell starts
if (Time.Current < HitObject.StartTime)
return false;
// Find the keyset which this key corresponds to
var keySet = rimKeys.Contains(key) ? rimKeys : centreKeys;
// Ensure alternating keysets
if (keySet == lastKeySet)
return false;
lastKeySet = keySet;
UpdateJudgement(true);
return true;
}
}
}

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

@ -0,0 +1,70 @@
// 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.Input;
using osu.Framework.Graphics;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using System.Collections.Generic;
using osu.Framework.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgement>
{
/// <summary>
/// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
/// These should be moved to bindings later.
/// </summary>
private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K });
protected DrawableTaikoHitObject(TaikoHitObject hitObject)
: base(hitObject)
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
}
protected override void LoadComplete()
{
LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2;
base.LoadComplete();
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
/// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between
/// a time value and the HitObject's StartTime.
/// </summary>
/// <param name="time"></param>
protected virtual void UpdateScrollPosition(double time)
{
MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
}
protected override void Update()
{
UpdateScrollPosition(Time.Current);
}
protected virtual bool HandleKeyPress(Key key) => false;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
// Make sure we don't handle held-down keys
if (args.Repeat)
return false;
// Check if we've pressed a valid taiko key
if (!validKeys.Contains(args.Key))
return false;
// Handle it!
return HandleKeyPress(args.Key);
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
{
/// <summary>
/// The symbol used for centre hit pieces.
/// </summary>
public class CentreHitSymbolPiece : CircularContainer
{
public CentreHitSymbolPiece()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(CirclePiece.SYMBOL_INNER_SIZE);
Masking = true;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both
}
};
}
}
}

View File

@ -0,0 +1,161 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics;
using System;
using osu.Game.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
{
/// <summary>
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
/// <para>
/// The body of this piece will overshoot its parent by <see cref="CirclePiece.Height"/> to form
/// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0.
/// </para>
/// </summary>
public class CirclePiece : Container, IHasAccentColour
{
public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f;
public const float SYMBOL_BORDER = 8;
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
private Color4 accentColour;
/// <summary>
/// The colour of the inner circle and outer glows.
/// </summary>
public Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
innerBackground.Colour = AccentColour;
triangles.ColourLight = AccentColour;
triangles.ColourDark = AccentColour.Darken(0.1f);
resetEdgeEffects();
}
}
private bool kiaiMode;
/// <summary>
/// Whether Kiai mode effects are enabled for this circle piece.
/// </summary>
public bool KiaiMode
{
get { return kiaiMode; }
set
{
kiaiMode = value;
resetEdgeEffects();
}
}
public override Anchor Origin
{
get { return Anchor.CentreLeft; }
set { throw new InvalidOperationException($"{nameof(CirclePiece)} must always use CentreLeft origin."); }
}
protected override Container<Framework.Graphics.Drawable> Content => SymbolContainer;
protected readonly Container SymbolContainer;
private readonly Container innerLayer;
private readonly Container innerCircleContainer;
private readonly Box innerBackground;
private readonly Triangles triangles;
public CirclePiece()
{
RelativeSizeAxes = Axes.X;
Height = TaikoHitObject.CIRCLE_RADIUS * 2;
// The "inner layer" is the body of the CirclePiece that overshoots it by Height/2 px on both sides
AddInternal(innerLayer = new Container
{
Name = "Inner Layer",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Framework.Graphics.Drawable[]
{
innerCircleContainer = new CircularContainer
{
Name = "Inner Circle",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Framework.Graphics.Drawable[]
{
innerBackground = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
},
triangles = new Triangles
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
}
},
new CircularContainer
{
Name = "Ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
BorderThickness = 8,
BorderColour = Color4.White,
Masking = true,
Children = new[]
{
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
SymbolContainer = new Container
{
Name = "Symbol",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
});
}
protected override void Update()
{
// Add the overshoot to compensate for corner radius
innerLayer.Width = DrawWidth + DrawHeight;
}
private void resetEdgeEffects()
{
innerCircleContainer.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = AccentColour,
Radius = KiaiMode ? 50 : 8
};
}
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
{
/// <summary>
/// The symbol used for rim hit pieces.
/// </summary>
public class RimHitSymbolPiece : CircularContainer
{
public RimHitSymbolPiece()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(CirclePiece.SYMBOL_SIZE);
BorderThickness = CirclePiece.SYMBOL_BORDER;
BorderColour = Color4.White;
Masking = true;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
};
}
}
}

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