Merge remote-tracking branch 'upstream/master' into ranks-section

This commit is contained in:
Dean Herbert 2017-09-12 19:38:46 +09:00
commit 28a25c201f
175 changed files with 3800 additions and 1866 deletions

@ -1 +1 @@
Subproject commit 14a33d110e2ed32e3a875bc2acd2bade244ba045 Subproject commit 1a259925b82c31ddcebf7b330a6ef9d3a9daf089

@ -1 +1 @@
Subproject commit f6042e1cb37cfad6c879d0e1245f7880c7fcd5f5 Subproject commit a4418111f8ed2350a6fd46fe69258884f0757745

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Deploy</RootNamespace> <RootNamespace>osu.Desktop.Deploy</RootNamespace>
<AssemblyName>osu.Desktop.Deploy</AssemblyName> <AssemblyName>osu.Desktop.Deploy</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup> </PropertyGroup>

View File

@ -12,50 +12,104 @@ namespace osu.Desktop.Tests.Visual
{ {
public override string Description => "BeatmapDetails tab of BeatmapDetailArea"; public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
private readonly BeatmapDetails details;
public TestCaseBeatmapDetails() public TestCaseBeatmapDetails()
{ {
BeatmapDetails details;
Add(details = new BeatmapDetails Add(details = new BeatmapDetails
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150), Padding = new MarginPadding(150),
Beatmap = new BeatmapInfo });
AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "All Metrics",
Metadata = new BeatmapMetadata
{ {
Version = "VisualTest", Source = "osu!lazer",
Metadata = new BeatmapMetadata Tags = "this beatmap has all the metrics",
{ },
Source = "Some guy", Difficulty = new BeatmapDifficulty
Tags = "beatmap metadata example with a very very long list of tags and not much creativity", {
}, CircleSize = 7,
Difficulty = new BeatmapDifficulty DrainRate = 1,
{ OverallDifficulty = 5.7f,
CircleSize = 7, ApproachRate = 3.5f,
ApproachRate = 3.5f, },
OverallDifficulty = 5.7f, StarDifficulty = 5.3f,
DrainRate = 1, Metrics = new BeatmapMetrics
}, {
StarDifficulty = 5.3f, Ratings = Enumerable.Range(0, 10),
Metrics = new BeatmapMetrics Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
{ Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
},
}, },
}); });
AddRepeatStep("fail values", newRetryAndFailValues, 10); AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo
} {
Version = "Only Ratings",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has ratings metrics but not retries or fails",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 6,
DrainRate = 9,
OverallDifficulty = 6,
ApproachRate = 6,
},
StarDifficulty = 4.8f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
},
});
private int lastRange = 1; AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Retries and Fails",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has retries and fails but no ratings",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 3.7f,
DrainRate = 6,
OverallDifficulty = 6,
ApproachRate = 7,
},
StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
private void newRetryAndFailValues() AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo
{ {
details.Beatmap.Metrics.Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6); Version = "No Metrics",
details.Beatmap.Metrics.Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6); Metadata = new BeatmapMetadata
details.Beatmap = details.Beatmap; {
lastRange += 100; Source = "osu!lazer",
Tags = "this beatmap has no metrics",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 5,
OverallDifficulty = 5.5f,
ApproachRate = 6.5f,
},
StarDifficulty = 1.97f,
Metrics = new BeatmapMetrics(),
});
AddStep("null beatmap", () => details.Beatmap = null);
} }
} }
} }

View File

@ -41,12 +41,14 @@ namespace osu.Desktop.Tests.Visual
{ {
new BeatmapSetInfo new BeatmapSetInfo
{ {
OnlineBeatmapSetID = 578332,
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
Title = @"OrVid", Title = @"OrVid",
Artist = @"An", Artist = @"An",
Author = @"RLC", Author = @"RLC",
Source = @"", Source = @"",
Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll",
}, },
OnlineInfo = new BeatmapSetOnlineInfo OnlineInfo = new BeatmapSetOnlineInfo
{ {
@ -71,12 +73,14 @@ namespace osu.Desktop.Tests.Visual
}, },
new BeatmapSetInfo new BeatmapSetInfo
{ {
OnlineBeatmapSetID = 599627,
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
Title = @"tiny lamp", Title = @"tiny lamp",
Artist = @"fhana", Artist = @"fhana",
Author = @"Sotarks", Author = @"Sotarks",
Source = @"ぎんぎつね", Source = @"ぎんぎつね",
Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration",
}, },
OnlineInfo = new BeatmapSetOnlineInfo OnlineInfo = new BeatmapSetOnlineInfo
{ {
@ -101,12 +105,14 @@ namespace osu.Desktop.Tests.Visual
}, },
new BeatmapSetInfo new BeatmapSetInfo
{ {
OnlineBeatmapSetID = 513268,
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
Title = @"At Gwanghwamun", Title = @"At Gwanghwamun",
Artist = @"KYUHYUN", Artist = @"KYUHYUN",
Author = @"Cerulean Veyron", Author = @"Cerulean Veyron",
Source = @"", Source = @"",
Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep",
}, },
OnlineInfo = new BeatmapSetOnlineInfo OnlineInfo = new BeatmapSetOnlineInfo
{ {
@ -146,12 +152,14 @@ namespace osu.Desktop.Tests.Visual
}, },
new BeatmapSetInfo new BeatmapSetInfo
{ {
OnlineBeatmapSetID = 586841,
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
Title = @"RHAPSODY OF BLUE SKY", Title = @"RHAPSODY OF BLUE SKY",
Artist = @"fhana", Artist = @"fhana",
Author = @"[Kamiya]", Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン", Source = @"小林さんちのメイドラゴン",
Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc",
}, },
OnlineInfo = new BeatmapSetOnlineInfo OnlineInfo = new BeatmapSetOnlineInfo
{ {

View File

@ -0,0 +1,84 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Menus;
namespace osu.Desktop.Tests.Visual
{
public class TestCaseEditorMenuBar : OsuTestCase
{
public TestCaseEditorMenuBar()
{
Add(new EditorMenuBar
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 50,
Items = new[]
{
new EditorMenuBarItem("File")
{
Items = new[]
{
new EditorMenuItem("Clear All Notes"),
new EditorMenuItem("Open Difficulty..."),
new EditorMenuItem("Save"),
new EditorMenuItem("Create a new Difficulty..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Revert to Saved"),
new EditorMenuItem("Revert to Saved (Full)"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Test Beatmap"),
new EditorMenuItem("Open AiMod"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Upload Beatmap..."),
new EditorMenuItem("Export Package"),
new EditorMenuItem("Export Map Package"),
new EditorMenuItem("Import from..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Open Song Folder"),
new EditorMenuItem("Open .osu in Notepad"),
new EditorMenuItem("Open .osb in Notepad"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Exit"),
}
},
new EditorMenuBarItem("Timing")
{
Items = new[]
{
new EditorMenuItem("Time Signature"),
new EditorMenuItem("Metronome Clicks"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Add Timing Section"),
new EditorMenuItem("Add Inheriting Section"),
new EditorMenuItem("Reset Current Section"),
new EditorMenuItem("Delete Timing Section"),
new EditorMenuItem("Resnap Current Section"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Timing Setup"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive),
new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive),
new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive),
new EditorMenuItemSpacer(),
new EditorMenuItem("Set Current Position as Preview Point"),
}
},
new EditorMenuBarItem("Testing")
{
Items = new[]
{
new EditorMenuItem("Item 1"),
new EditorMenuItem("Item 2"),
new EditorMenuItem("Item 3"),
}
},
}
});
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using OpenTK; using OpenTK;
@ -110,10 +109,7 @@ namespace osu.Desktop.Tests.Visual
h.Depth = depth++; h.Depth = depth++;
if (auto) if (auto)
{
h.State = ArmedState.Hit; h.State = ArmedState.Hit;
h.Judgement = new OsuJudgement { Result = HitResult.Hit };
}
playfieldContainer.Add(h); playfieldContainer.Add(h);
var proxyable = h as IDrawableHitObjectWithProxiedApproach; var proxyable = h as IDrawableHitObjectWithProxiedApproach;

View File

@ -1,13 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
using OpenTK;
namespace osu.Desktop.Tests.Visual namespace osu.Desktop.Tests.Visual
{ {
@ -24,7 +24,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.XH, Rank = ScoreRank.XH,
Accuracy = 100, Accuracy = 1,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -42,7 +42,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.X, Rank = ScoreRank.X,
Accuracy = 100, Accuracy = 1,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -60,7 +60,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.SH, Rank = ScoreRank.SH,
Accuracy = 100, Accuracy = 1,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -78,7 +78,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.S, Rank = ScoreRank.S,
Accuracy = 100, Accuracy = 1,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -96,7 +96,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.A, Rank = ScoreRank.A,
Accuracy = 100, Accuracy = 1,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -114,7 +114,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.B, Rank = ScoreRank.B,
Accuracy = 98.26, Accuracy = 0.9826,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -132,7 +132,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.C, Rank = ScoreRank.C,
Accuracy = 96.54, Accuracy = 0.9654,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -150,7 +150,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.F, Rank = ScoreRank.F,
Accuracy = 60.25, Accuracy = 0.6025,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -168,7 +168,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.F, Rank = ScoreRank.F,
Accuracy = 51.40, Accuracy = 0.5140,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -186,7 +186,7 @@ namespace osu.Desktop.Tests.Visual
new Score new Score
{ {
Rank = ScoreRank.F, Rank = ScoreRank.F,
Accuracy = 42.22, Accuracy = 0.4222,
MaxCombo = 244, MaxCombo = 244,
TotalScore = 1707827, TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), }, Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },

View File

@ -41,8 +41,20 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000), RelativeChildSize = new Vector2(1, 10000),
Children = new[] Children = new[]
{ {
new DrawableNote(new Note { StartTime = 5000 }, ManiaAction.Key1) { AccentColour = Color4.Red }, new DrawableNote(new Note(), ManiaAction.Key1)
new DrawableNote(new Note { StartTime = 6000 }, ManiaAction.Key1) { AccentColour = Color4.Red } {
Y = 5000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
},
new DrawableNote(new Note(), ManiaAction.Key1)
{
Y = 6000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
}
} }
} }
} }
@ -63,11 +75,14 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000), RelativeChildSize = new Vector2(1, 10000),
Children = new[] Children = new[]
{ {
new DrawableHoldNote(new HoldNote new DrawableHoldNote(new HoldNote(), ManiaAction.Key1)
{ {
StartTime = 5000, Y = 5000,
Duration = 1000 Height = 1000,
}, ManiaAction.Key1) { AccentColour = Color4.Red } LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
@ -13,6 +14,8 @@ using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Desktop.Tests.Visual namespace osu.Desktop.Tests.Visual
{ {
@ -29,6 +32,8 @@ namespace osu.Desktop.Tests.Visual
public TestCaseManiaPlayfield() public TestCaseManiaPlayfield()
{ {
var rng = new Random(1337);
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal)); AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal)); AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left)); AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
@ -43,6 +48,20 @@ namespace osu.Desktop.Tests.Visual
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true)); AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true)); AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true)); AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
AddStep("Hit explosion", () =>
{
var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
int col = rng.Next(0, 4);
var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1)
{
AccentColour = playfield.Columns.ElementAt(col).AccentColour
};
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
});
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -56,7 +75,7 @@ namespace osu.Desktop.Tests.Visual
TimingPoint = { BeatLength = 1000 } TimingPoint = { BeatLength = 1000 }
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic); }, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
private void createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false) private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
{ {
Clear(); Clear();
@ -72,6 +91,8 @@ namespace osu.Desktop.Tests.Visual
}); });
playfield.Inverted.Value = inverted; playfield.Inverted.Value = inverted;
return playfield;
} }
private void createPlayfieldWithNotes(bool gravity, bool inverted = false) private void createPlayfieldWithNotes(bool gravity, bool inverted = false)

View File

@ -32,7 +32,7 @@ namespace osu.Desktop.Tests.Visual
backingDatabase.CreateTable<StoreVersion>(); backingDatabase.CreateTable<StoreVersion>();
rulesets = new RulesetStore(backingDatabase); rulesets = new RulesetStore(backingDatabase);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets); manager = new BeatmapManager(storage, null, backingDatabase, rulesets, null);
for (int i = 0; i < 100; i += 10) for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i)); manager.Import(createTestBeatmapSet(i));

View File

@ -1,18 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Desktop.Tests.Beatmaps; using osu.Desktop.Tests.Beatmaps;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Rulesets.Mods;
using System.Linq;
using osu.Game.Beatmaps.Formats;
using System.Text;
using System.IO;
namespace osu.Desktop.Tests.Visual namespace osu.Desktop.Tests.Visual
{ {
@ -33,55 +33,739 @@ namespace osu.Desktop.Tests.Visual
{ {
base.LoadComplete(); base.LoadComplete();
var objects = new List<HitObject>();
int time = 1500;
for (int i = 0; i < 50; i++)
{
objects.Add(new HitCircle
{
StartTime = time,
Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : OsuPlayfield.BASE_SIZE.X,
i % 4 < 2 ? 0 : OsuPlayfield.BASE_SIZE.Y),
NewCombo = i % 4 == 0
});
time += 500;
}
Beatmap b = new Beatmap
{
HitObjects = objects,
BeatmapInfo = new BeatmapInfo
{
Difficulty = new BeatmapDifficulty(),
Ruleset = rulesets.Query<RulesetInfo>().First(),
Metadata = new BeatmapMetadata
{
Artist = @"Unknown",
Title = @"Sample Beatmap",
Author = @"peppy",
}
}
};
WorkingBeatmap beatmap = new TestWorkingBeatmap(b);
Add(new Box Add(new Box
{ {
RelativeSizeAxes = Framework.Graphics.Axes.Both, RelativeSizeAxes = Framework.Graphics.Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
}); });
Add(Player = CreatePlayer(beatmap)); foreach (var r in rulesets.Query<RulesetInfo>())
AddStep(r.Name, () => loadPlayerFor(r));
loadPlayerFor(rulesets.Query<RulesetInfo>().First());
} }
protected virtual Player CreatePlayer(WorkingBeatmap beatmap) private void loadPlayerFor(RulesetInfo r)
{
Beatmap beatmap;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data)))
using (var reader = new StreamReader(stream))
beatmap = BeatmapDecoder.GetDecoder(reader).Decode(reader);
beatmap.BeatmapInfo.Ruleset = r;
var instance = r.CreateInstance();
WorkingBeatmap working = new TestWorkingBeatmap(beatmap);
working.Mods.Value = new[] { instance.GetAllMods().First(m => m is ModNoFail) };
if (Player != null)
Remove(Player);
Add(Player = CreatePlayer(working, instance));
}
protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{ {
return new Player return new Player
{ {
InitialBeatmap = beatmap InitialBeatmap = beatmap
}; };
} }
private const string test_beatmap_data =
@"osu file format v14
[General]
AudioLeadIn: 500
PreviewTime: 53498
Countdown: 0
SampleSet: Soft
StackLeniency: 0.7
Mode: 0
LetterboxInBreaks: 0
WidescreenStoryboard: 1
[Editor]
DistanceSpacing: 1.2
BeatDivisor: 4
GridSize: 4
TimelineZoom: 1
[Metadata]
Title:My Love
TitleUnicode:My Love
Artist:Kuba Oms
ArtistUnicode:Kuba Oms
Creator:W h i t e
Version:Hard
Source:ADHD
Tags:Monthly Beatmapping Contest Electronic folk pop w_h_i_t_e
BeatmapID:397534
BeatmapSetID:163112
[Difficulty]
HPDrainRate:5
CircleSize:4
OverallDifficulty:6
ApproachRate:7
SliderMultiplier:1.44
SliderTickRate:2
[Events]
//Break Periods
2,69870,83770
2,152170,158770
//Storyboard Layer 0 (Background)
//Storyboard Layer 1 (Fail)
//Storyboard Layer 2 (Pass)
//Storyboard Layer 3 (Foreground)
//Storyboard Sound Samples
[TimingPoints]
2170,468.75,4,2,0,40,1,0
4045,-100,4,2,0,30,0,0
4162,-100,4,2,0,40,0,0
5920,-100,4,2,0,30,0,0
6037,-100,4,2,0,40,0,0
7795,-100,4,2,0,30,0,0
7912,-100,4,2,0,40,0,0
9670,-100,4,2,0,40,0,0
9787,-100,4,2,0,50,0,0
11545,-100,4,2,0,40,0,0
11662,-100,4,2,0,50,0,0
13420,-100,4,2,0,40,0,0
13537,-100,4,2,0,50,0,0
15295,-100,4,2,0,40,0,0
15412,-100,4,2,0,50,0,0
17170,-100,4,2,0,40,0,0
17287,-100,4,2,0,50,0,0
19045,-100,4,2,0,40,0,0
19162,-100,4,2,0,50,0,0
20920,-100,4,2,0,40,0,0
21037,-100,4,2,0,50,0,0
22795,-100,4,2,0,40,0,0
22912,-100,4,2,0,50,0,0
24670,-100,4,2,0,70,0,0
37560,-200,4,2,0,30,0,0
38263,-200,4,2,0,5,0,0
38966,-100,4,2,0,30,0,0
39670,-100,4,2,0,70,0,0
53732,-100,4,2,0,40,0,0
54670,-100,4,2,0,80,0,1
55138,-100,4,2,0,60,0,1
55255,-100,4,2,0,80,0,1
56076,-100,4,2,0,60,0,1
56193,-100,4,2,0,80,0,1
57013,-100,4,2,0,60,0,1
57130,-100,4,2,0,80,0,1
57951,-100,4,2,0,60,0,1
58068,-100,4,2,0,80,0,1
58888,-100,4,2,0,60,0,1
59005,-100,4,2,0,80,0,1
59826,-100,4,2,0,60,0,1
59943,-100,4,2,0,80,0,1
60763,-100,4,2,0,60,0,1
60880,-100,4,2,0,80,0,1
61701,-100,4,2,0,60,0,1
61818,-100,4,2,0,80,0,1
62638,-100,4,2,0,60,0,1
62755,-100,4,2,0,80,0,1
63576,-100,4,2,0,60,0,1
63693,-100,4,2,0,80,0,1
64513,-100,4,2,0,60,0,1
64630,-100,4,2,0,80,0,1
65451,-100,4,2,0,60,0,1
65568,-100,4,2,0,80,0,1
66388,-100,4,2,0,60,0,1
66505,-100,4,2,0,80,0,1
67326,-100,4,2,0,60,0,1
67443,-100,4,2,0,80,0,1
68263,-100,4,2,0,60,0,1
68380,-100,4,2,0,80,0,1
69201,-100,4,2,0,60,0,1
69318,-100,4,2,0,80,0,1
69670,-100,4,2,0,70,0,0
84670,-100,4,2,0,70,0,0
97560,-200,4,2,0,70,0,0
97795,-200,4,2,0,30,0,0
98966,-100,4,2,0,30,0,0
99670,-100,4,2,0,70,0,0
113732,-100,4,2,0,40,0,0
114670,-100,4,2,0,80,0,1
115138,-100,4,2,0,60,0,1
115255,-100,4,2,0,80,0,1
116076,-100,4,2,0,60,0,1
116193,-100,4,2,0,80,0,1
117013,-100,4,2,0,60,0,1
117130,-100,4,2,0,80,0,1
117951,-100,4,2,0,60,0,1
118068,-100,4,2,0,80,0,1
118888,-100,4,2,0,60,0,1
119005,-100,4,2,0,80,0,1
119826,-100,4,2,0,60,0,1
119943,-100,4,2,0,80,0,1
120763,-100,4,2,0,60,0,1
120880,-100,4,2,0,80,0,1
121701,-100,4,2,0,60,0,1
121818,-100,4,2,0,80,0,1
122638,-100,4,2,0,60,0,1
122755,-100,4,2,0,80,0,1
123576,-100,4,2,0,60,0,1
123693,-100,4,2,0,80,0,1
124513,-100,4,2,0,60,0,1
124630,-100,4,2,0,80,0,1
125451,-100,4,2,0,60,0,1
125568,-100,4,2,0,80,0,1
126388,-100,4,2,0,60,0,1
126505,-100,4,2,0,80,0,1
127326,-100,4,2,0,60,0,1
127443,-100,4,2,0,80,0,1
128263,-100,4,2,0,60,0,1
128380,-100,4,2,0,80,0,1
129201,-100,4,2,0,60,0,1
129318,-100,4,2,0,80,0,1
129670,-200,4,2,0,40,0,0
144670,-133.333333333333,4,2,0,40,0,0
159670,-133.333333333333,4,2,0,40,0,0
163420,-133.333333333333,4,2,0,45,0,0
163888,-125,4,2,0,50,0,0
164357,-117.647058823529,4,2,0,55,0,0
164826,-111.111111111111,4,2,0,60,0,0
165295,-105.263157894737,4,2,0,65,0,0
165763,-100,4,2,0,70,0,0
166232,-100,4,2,0,40,0,0
167170,-100,4,2,0,80,0,1
167638,-100,4,2,0,60,0,1
167755,-100,4,2,0,80,0,1
168576,-100,4,2,0,60,0,1
168693,-100,4,2,0,80,0,1
169513,-100,4,2,0,60,0,1
169630,-100,4,2,0,80,0,1
170451,-100,4,2,0,60,0,1
170568,-100,4,2,0,80,0,1
171388,-100,4,2,0,60,0,1
171505,-100,4,2,0,80,0,1
172326,-100,4,2,0,60,0,1
172443,-100,4,2,0,80,0,1
173263,-100,4,2,0,60,0,1
173380,-100,4,2,0,80,0,1
174201,-100,4,2,0,60,0,1
174318,-100,4,2,0,80,0,1
175138,-100,4,2,0,60,0,1
175255,-100,4,2,0,80,0,1
176076,-100,4,2,0,60,0,1
176193,-100,4,2,0,80,0,1
177013,-100,4,2,0,60,0,1
177130,-100,4,2,0,80,0,1
177951,-100,4,2,0,60,0,1
178068,-100,4,2,0,80,0,1
178888,-100,4,2,0,60,0,1
179005,-100,4,2,0,80,0,1
179826,-100,4,2,0,60,0,1
179943,-100,4,2,0,80,0,1
180763,-100,4,2,0,60,0,1
180880,-100,4,2,0,80,0,1
180998,-100,4,2,0,80,0,0
181466,-100,4,2,0,60,0,0
181584,-100,4,2,0,80,0,0
181935,-100,4,2,0,80,0,0
182170,-100,4,2,0,80,0,1
182638,-100,4,2,0,60,0,1
182755,-100,4,2,0,80,0,1
183576,-100,4,2,0,60,0,1
183693,-100,4,2,0,80,0,1
184513,-100,4,2,0,60,0,1
184630,-100,4,2,0,80,0,1
185451,-100,4,2,0,60,0,1
185568,-100,4,2,0,80,0,1
186388,-100,4,2,0,60,0,1
186505,-100,4,2,0,80,0,1
187326,-100,4,2,0,60,0,1
187443,-100,4,2,0,80,0,1
188263,-100,4,2,0,60,0,1
188380,-100,4,2,0,80,0,1
189201,-100,4,2,0,60,0,1
189318,-100,4,2,0,80,0,1
190138,-100,4,2,0,60,0,1
190255,-100,4,2,0,80,0,1
191076,-100,4,2,0,60,0,1
191193,-100,4,2,0,80,0,1
192013,-100,4,2,0,60,0,1
192130,-100,4,2,0,80,0,1
192951,-100,4,2,0,60,0,1
193068,-100,4,2,0,80,0,1
193888,-100,4,2,0,60,0,1
194005,-100,4,2,0,80,0,1
194826,-100,4,2,0,60,0,1
194943,-100,4,2,0,80,0,1
195295,-100,4,2,0,50,0,1
195529,-100,4,2,0,52,0,1
195646,-100,4,2,0,54,0,1
195763,-100,4,2,0,56,0,1
195880,-100,4,2,0,58,0,1
195998,-100,4,2,0,60,0,1
196115,-100,4,2,0,62,0,1
196232,-100,4,2,0,64,0,1
196349,-100,4,2,0,68,0,1
196466,-100,4,2,0,70,0,1
196584,-100,4,2,0,72,0,1
196701,-100,4,2,0,74,0,1
196818,-100,4,2,0,76,0,1
196935,-100,4,2,0,78,0,1
197052,-100,4,2,0,80,0,1
197170,-100,4,2,0,80,0,0
197873,-100,4,2,0,60,0,0
197990,-100,4,2,0,80,0,0
198341,-100,4,2,0,60,0,0
199045,-100,4,2,0,80,0,0
199279,-100,4,2,0,60,0,0
199630,-100,4,2,0,80,0,0
200216,-100,4,2,0,60,0,0
200334,-100,4,2,0,80,0,0
201623,-100,4,2,0,60,0,0
201740,-100,4,2,0,80,0,0
202326,-100,4,2,0,60,0,0
202443,-100,4,2,0,80,0,0
203029,-100,4,2,0,60,0,0
203498,-100,4,2,0,80,0,0
203966,-100,4,2,0,60,0,0
204201,-100,4,2,0,80,0,0
205373,-100,4,2,0,60,0,0
205490,-100,4,2,0,80,0,0
205841,-100,4,2,0,60,0,0
206076,-100,4,2,0,60,0,0
206545,-100,4,2,0,80,0,0
206779,-100,4,2,0,60,0,0
207130,-100,4,2,0,80,0,0
207716,-100,4,2,0,60,0,0
207951,-100,4,2,0,80,0,0
209123,-100,4,2,0,60,0,0
209240,-100,4,2,0,80,0,0
209826,-100,4,2,0,60,0,0
209943,-100,4,2,0,80,0,0
210529,-100,4,2,0,60,0,0
210880,-100,4,2,0,80,0,0
211232,-100,4,2,0,60,0,0
211701,-100,4,2,0,70,0,0
212170,-100,4,2,0,80,0,0
212873,-100,4,2,0,60,0,0
212990,-100,4,2,0,80,0,0
213341,-100,4,2,0,60,0,0
213576,-100,4,2,0,60,0,0
214045,-100,4,2,0,80,0,0
214279,-100,4,2,0,60,0,0
214630,-100,4,2,0,80,0,0
215216,-100,4,2,0,60,0,0
215451,-100,4,2,0,80,0,0
216623,-100,4,2,0,60,0,0
216740,-100,4,2,0,80,0,0
217326,-100,4,2,0,60,0,0
217443,-100,4,2,0,80,0,0
218029,-100,4,2,0,60,0,0
218498,-100,4,2,0,80,0,0
218732,-100,4,2,0,50,0,0
219670,-100,4,2,0,70,0,0
220138,-100,4,2,0,65,0,0
220373,-100,4,2,0,45,0,0
220490,-100,4,2,0,65,0,0
220607,-100,4,2,0,60,0,0
220841,-100,4,2,0,35,0,0
221076,-100,4,2,0,35,0,0
221545,-100,4,2,0,50,0,0
221779,-100,4,2,0,30,0,0
222013,-111.111111111111,4,2,0,25,0,0
222130,-111.111111111111,4,2,0,40,0,0
222482,-125,4,2,0,40,0,0
222716,-125,4,2,0,20,0,0
222951,-100,4,2,0,15,0,0
223420,-100,4,2,0,30,0,0
224357,-100,4,2,0,25,0,0
225295,-100,4,2,0,20,0,0
226232,-100,4,2,0,15,0,0
226701,-100,4,2,0,10,0,0
227170,-100,4,2,0,5,0,0
[Colours]
Combo1 : 17,254,176
Combo2 : 173,255,95
Combo3 : 255,88,100
Combo4 : 255,94,55
[HitObjects]
320,256,2170,6,0,P|256:284|192:256,1,144,4|0,0:0|0:0,0:0:0:0:
144,184,2873,1,0,0:0:0:0:
108,260,3107,2,0,P|112:296|100:336,1,72
28,288,3576,2,0,P|24:252|36:212,1,72,0|0,0:0|0:0,0:0:0:0:
76,140,4045,6,0,L|220:136,1,144,4|0,0:0|0:0,0:0:0:0:
292,88,4748,1,0,0:0:0:0:
292,88,4982,2,0,P|304:120|300:168,1,72
388,168,5451,2,0,P|396:133|416:103,1,72,0|0,0:0|0:0,0:0:0:0:
472,172,5920,6,0,B|470:200|457:222|457:222|488:256|476:308,1,144,4|0,0:0|0:0,0:0:0:0:
396,280,6623,1,0,0:0:0:0:
324,328,6857,2,0,P|288:332|252:324,1,72
180,280,7326,2,0,L|108:284,1,72,0|0,0:0|0:0,0:0:0:0:
256,192,7795,12,0,9670,0:0:0:0:
428,212,10138,1,0,0:0:0:0:
292,320,10607,1,0,0:0:0:0:
184,184,11076,2,0,L|112:180,1,72,0|0,0:0|0:0,0:0:0:0:
24,172,11545,5,6,0:0:0:0:
160,280,12013,1,0,0:0:0:0:
268,144,12482,1,0,0:0:0:0:
132,36,12951,2,0,L|204:32,1,72,0|0,0:0|0:0,0:0:0:0:
284,60,13420,6,0,P|340:100|344:180,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0:
268,144,14591,1,0,0:0:0:0:
284,228,14826,2,0,P|316:248|364:252,1,72,0|0,0:0|0:0,0:0:0:0:
436,248,15295,6,0,P|372:272|344:340,1,144,6|2,0:0|0:0,0:0:0:0:
168,338,16232,2,0,P|141:273|76:248,1,144,2|2,0:0|0:0,0:0:0:0:
4,296,16935,1,0,0:0:0:0:
80,336,17170,5,6,0:0:0:0:
44,168,17638,1,0,0:0:0:0:
212,128,18107,1,0,0:0:0:0:
248,296,18576,2,0,P|284:288|320:292,1,72,0|0,0:0|0:0,0:0:0:0:
400,324,19045,5,6,0:0:0:0:
280,200,19513,1,0,0:0:0:0:
368,52,19982,1,0,0:0:0:0:
488,176,20451,2,0,P|452:168|416:172,1,72,0|0,0:0|0:0,0:0:0:0:
336,200,20920,6,0,P|284:216|200:192,1,144,6|0,0:0|0:0,0:0:0:0:
200,192,21857,2,0,L|204:264,1,72,0|0,0:3|0:0,0:0:0:0:
117,244,22326,2,0,L|120:172,1,72,0|0,0:0|0:0,0:0:0:0:
40,152,22795,6,0,L|28:296,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0:
152,24,24201,1,0,0:0:0:0:
220,76,24435,1,0,3:0:0:0:
304,56,24670,6,0,P|288:120|296:196,1,144,4|2,0:3|0:3,0:0:0:0:
344,268,25373,1,0,0:0:0:0:
416,316,25607,2,0,P|452:312|508:316,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
244,344,26545,6,0,P|176:356|108:328,1,144,4|2,0:3|0:3,0:0:0:0:
60,256,27248,1,0,0:0:0:0:
36,172,27482,2,0,L|40:100,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
188,252,28420,6,0,P|192:184|196:100,1,144,4|2,0:3|0:3,0:0:0:0:
140,40,29123,1,0,0:0:0:0:
140,40,29357,2,0,B|172:16|220:24|220:24|288:36,1,144,0|2,0:0|0:3,0:0:0:0:
364,52,30060,1,0,0:0:0:0:
308,116,30295,6,0,B|300:168|300:168|328:256,1,144,4|2,0:3|0:3,0:0:0:0:
340,340,30998,1,0,0:0:0:0:
260,308,31232,2,0,L|188:304,1,72,0|2,0:0|0:3,0:0:0:0:
100,296,31701,1,2,0:3:0:0:
136,374,31935,1,0,0:0:0:0:
152,224,32170,6,0,P|160:152|132:88,1,144,4|2,0:3|0:3,0:0:0:0:
56,48,32873,1,0,0:0:0:0:
60,136,33107,2,0,L|56:208,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
224,76,34045,6,0,P|289:104|360:96,1,144,4|2,0:3|0:3,0:0:0:0:
432,48,34748,1,0,0:0:0:0:
440,132,34982,2,0,B|432:156|432:156|436:204,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
448,304,35920,6,0,B|412:315|380:292|380:292|348:269|312:280,1,144,4|2,0:3|0:3,0:0:0:0:
332,364,36623,1,0,0:0:0:0:
247,339,36857,2,0,P|230:308|225:273,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
312,280,37560,6,0,L|316:172,1,108
134,35,38966,5,0,0:0:0:0:
72,96,39201,2,0,P|119:119|171:111,1,108,0|0,0:0|0:0,0:0:0:0:
192,100,39670,6,0,L|200:172,1,72,4|2,0:0|0:0,0:0:0:0:
147,240,40138,2,0,P|133:272|132:308,1,72,0|2,1:0|0:0,0:0:0:0:
216,292,40607,2,0,B|260:308|260:308|356:292,1,144,4|0,2:3|1:0,1:0:0:0:
356,292,41310,1,2,0:0:0:0:
436,327,41545,6,0,P|441:292|435:257,1,72,4|2,0:3|0:0,0:0:0:0:
364,204,42013,2,0,P|336:144|352:68,1,144,0|4,1:0|2:3,1:0:0:0:
404,0,42716,1,2,0:0:0:0:
440,80,42951,2,0,B|464:84|464:84|512:80,1,72,0|2,1:0|0:0,0:0:0:0:
351,71,43420,6,0,B|296:68|296:68|268:76|268:76|196:72,1,144,4|0,2:3|1:0,1:0:0:0:
120,68,44123,1,2,0:0:0:0:
160,144,44357,2,0,P|172:180|168:232,1,72,4|2,0:3|0:0,0:0:0:0:
76,264,44826,2,0,P|76:228|88:194,1,72,0|2,1:0|0:0,0:0:0:0:
160,144,45295,5,4,0:3:0:0:
244,164,45529,1,2,0:0:0:0:
268,248,45763,2,0,L|344:252,1,72,0|2,1:0|0:0,0:0:0:0:
408,156,46232,2,0,L|336:159,1,72,4|2,0:3|0:0,0:0:0:0:
212,72,46701,2,0,L|288:76,1,72,0|2,1:0|0:0,0:0:0:0:
400,72,47170,6,0,P|464:96|488:172,1,144,4|0,2:0|1:0,1:0:0:0:
476,248,47873,1,2,0:0:0:0:
436,324,48107,2,0,L|284:320,1,144,4|0,2:3|1:0,1:0:0:0:
204,316,48810,1,2,0:0:0:0:
127,355,49045,6,0,P|120:321|124:285,1,72,4|2,0:3|0:0,0:0:0:0:
192,232,49513,2,0,L|335:228,1,144,0|4,1:0|2:3,1:0:0:0:
412,188,50216,1,2,0:0:0:0:
444,108,50451,2,0,P|452:72|448:36,1,72,0|2,1:0|0:0,0:0:0:0:
368,68,50920,6,0,B|332:79|300:56|300:56|268:33|232:44,1,144,4|0,2:3|1:0,1:0:0:0:
152,76,51623,1,2,0:0:0:0:
76,116,51857,2,0,L|80:268,1,144,4|0,2:3|1:0,1:0:0:0:
80,260,52560,1,2,0:0:0:0:
8,308,52795,6,0,P|34:334|69:346,1,72,4|2,0:3|0:0,0:0:0:0:
148,312,53263,2,0,P|163:278|162:241,1,72,0|2,1:0|0:0,0:0:0:0:
156,156,53732,5,0,3:0:0:0:
156,156,53966,1,2,0:0:0:0:
236,196,54201,2,0,L|312:192,1,72,8|0,0:3|0:0,0:0:0:0:
368,256,54670,6,0,P|392:216|352:116,1,144,4|2,0:0|1:2,0:0:0:0:
288,92,55373,1,0,0:0:0:0:
360,40,55607,2,0,L|432:36,1,72,4|0,0:3|3:0,0:0:0:0:
288,92,56076,2,0,L|216:88,1,72,2|0,1:2|0:0,0:0:0:0:
132,72,56545,6,0,P|172:88|200:184,1,144,4|2,0:3|1:2,0:0:0:0:
143,241,57248,1,0,0:0:0:0:
65,202,57482,2,0,P|87:174|119:157,1,72,4|0,0:3|3:0,0:0:0:0:
132,324,57951,2,0,P|98:312|72:288,1,72,2|0,1:2|0:0,0:0:0:0:
143,241,58420,6,0,L|288:240,1,144,4|2,0:3|1:2,0:0:0:0:
372,240,59123,1,0,0:0:0:0:
330,314,59357,2,0,P|318:350|322:390,1,72,4|0,0:3|3:0,0:0:0:0:
452,264,59826,2,0,P|453:228|442:194,1,72,2|0,1:2|0:0,0:0:0:0:
384,128,60295,6,0,B|336:144|336:144|244:128,1,144,4|2,0:3|1:2,0:0:0:0:
164,160,60998,2,0,P|160:116|168:88,1,72,0|4,0:0|0:3,0:0:0:0:
244,128,61466,2,0,P|248:172|240:200,1,72,0|2,3:0|1:2,0:0:0:0:
168,248,61935,1,0,0:0:0:0:
120,320,62170,6,0,P|196:328|252:272,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
80,244,63341,1,0,3:0:0:0:
100,160,63576,2,0,L|24:156,1,72,2|0,1:2|0:0,0:0:0:0:
180,128,64045,6,0,P|249:138|304:94,1,144,4|2,0:3|1:2,0:0:0:0:
226,57,64748,1,0,0:0:0:0:
304,94,64982,2,0,L|300:166,1,72,4|0,0:3|3:0,0:0:0:0:
377,203,65451,2,0,L|388:132,1,72,2|0,1:2|0:0,0:0:0:0:
468,180,65920,6,0,L|432:328,1,144,4|2,0:3|1:2,0:0:0:0:
276,252,66857,2,0,P|208:248|140:280,1,144,4|2,0:3|1:2,0:0:0:0:
84,344,67560,1,0,0:0:0:0:
56,260,67795,6,0,L|52:188,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
168,128,68732,2,0,L|172:56,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
244,168,69435,1,0,0:0:0:0:
332,164,69670,1,4,0:3:0:0:
208,328,84670,6,0,P|224:264|216:188,1,144,4|2,0:3|0:3,0:0:0:0:
168,116,85373,1,0,0:0:0:0:
96,68,85607,2,0,P|60:72|4:68,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
268,40,86545,6,0,P|336:28|404:56,1,144,4|2,0:3|0:3,0:0:0:0:
452,128,87248,1,0,0:0:0:0:
476,212,87482,2,0,L|472:284,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
324,132,88420,6,0,P|320:200|316:284,1,144,4|2,0:3|0:3,0:0:0:0:
372,344,89123,1,0,0:0:0:0:
372,344,89357,2,0,B|340:368|292:360|292:360|224:348,1,144,0|2,0:0|0:3,0:0:0:0:
148,332,90060,1,0,0:0:0:0:
204,268,90295,6,0,B|212:216|212:216|184:128,1,144,4|2,0:3|0:3,0:0:0:0:
172,44,90998,1,0,0:0:0:0:
252,76,91232,2,0,L|324:80,1,72,0|2,0:0|0:3,0:0:0:0:
412,88,91701,1,2,0:3:0:0:
377,9,91935,1,0,0:0:0:0:
360,160,92170,6,0,P|352:232|380:296,1,144,4|2,0:3|0:3,0:0:0:0:
456,336,92873,1,0,0:0:0:0:
452,248,93107,2,0,L|456:176,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
288,308,94045,6,0,P|223:280|152:288,1,144,4|2,0:3|0:3,0:0:0:0:
80,336,94748,1,0,0:0:0:0:
72,252,94982,2,0,B|80:228|80:228|76:180,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
64,80,95920,6,0,B|100:69|132:92|132:92|164:115|200:104,1,144,4|2,0:3|0:3,0:0:0:0:
180,20,96623,1,0,0:0:0:0:
265,45,96857,2,0,P|282:76|287:111,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
200,104,97560,1,0,0:0:0:0:
200,104,97677,1,0,0:0:0:0:
200,104,97795,6,0,B|196:142|217:166|217:166|176:180|160:220,1,144,4|0,0:3|0:0,0:0:0:0:
240,248,98966,5,0,0:0:0:0:
202,325,99201,2,0,P|254:333|301:309,1,108,0|0,0:0|0:0,0:0:0:0:
315,292,99670,6,0,L|323:220,1,72,4|2,0:0|0:0,0:0:0:0:
365,144,100138,2,0,P|379:112|380:76,1,72,0|2,1:0|0:0,0:0:0:0:
296,92,100607,2,0,B|252:76|252:76|156:92,1,144,4|0,2:3|1:0,1:0:0:0:
156,92,101310,1,2,0:0:0:0:
76,57,101545,6,0,P|71:92|77:127,1,72,4|2,0:3|0:0,0:0:0:0:
148,180,102013,2,0,P|176:240|160:316,1,144,0|4,1:0|2:3,1:0:0:0:
108,384,102716,1,2,0:0:0:0:
72,304,102951,2,0,B|48:300|48:300|0:304,1,72,0|2,1:0|0:0,0:0:0:0:
161,313,103420,6,0,B|216:316|216:316|244:308|244:308|316:312,1,144,4|0,2:3|1:0,1:0:0:0:
392,316,104123,1,2,0:0:0:0:
352,240,104357,2,0,P|340:204|344:152,1,72,4|2,0:3|0:0,0:0:0:0:
436,120,104826,2,0,P|436:156|424:190,1,72,0|2,1:0|0:0,0:0:0:0:
352,240,105295,5,4,0:3:0:0:
268,220,105529,1,2,0:0:0:0:
244,136,105763,2,0,L|168:132,1,72,0|2,1:0|0:0,0:0:0:0:
104,228,106232,2,0,L|176:225,1,72,4|2,0:3|0:0,0:0:0:0:
300,312,106701,2,0,L|224:308,1,72,0|2,1:0|0:0,0:0:0:0:
112,312,107170,6,0,P|48:288|24:212,1,144,4|0,2:0|1:0,1:0:0:0:
36,136,107873,1,2,0:0:0:0:
76,60,108107,2,0,L|228:64,1,144,4|0,2:3|1:0,1:0:0:0:
308,68,108810,1,2,0:0:0:0:
385,29,109045,6,0,P|392:63|388:99,1,72,4|2,0:3|0:0,0:0:0:0:
320,152,109513,2,0,L|177:156,1,144,0|4,1:0|2:3,1:0:0:0:
100,196,110216,1,2,0:0:0:0:
68,276,110451,2,0,P|60:312|64:348,1,72,0|2,1:0|0:0,0:0:0:0:
144,316,110920,6,0,B|180:305|212:328|212:328|244:351|280:340,1,144,4|0,2:3|1:0,1:0:0:0:
360,308,111623,1,2,0:0:0:0:
436,268,111857,2,0,L|432:116,1,144,4|0,2:3|1:0,1:0:0:0:
432,124,112560,1,2,0:0:0:0:
504,76,112795,6,0,P|478:50|443:38,1,72,4|2,0:3|0:0,0:0:0:0:
364,72,113263,2,0,P|349:106|350:143,1,72,0|2,1:0|0:0,0:0:0:0:
356,228,113732,5,0,3:0:0:0:
356,228,113966,1,2,0:0:0:0:
276,188,114201,2,0,L|200:192,1,72,8|0,0:3|0:0,0:0:0:0:
144,128,114670,6,0,P|120:168|160:268,1,144,4|2,0:0|1:2,0:0:0:0:
224,292,115373,1,0,0:0:0:0:
152,344,115607,2,0,L|80:348,1,72,4|0,0:3|3:0,0:0:0:0:
224,292,116076,2,0,L|296:296,1,72,2|0,1:2|0:0,0:0:0:0:
380,312,116545,6,0,P|340:296|312:200,1,144,4|2,0:3|1:2,0:0:0:0:
369,143,117248,1,0,0:0:0:0:
447,182,117482,2,0,P|425:210|393:227,1,72,4|0,0:3|3:0,0:0:0:0:
380,60,117951,2,0,P|414:72|440:96,1,72,2|0,1:2|0:0,0:0:0:0:
369,143,118420,6,0,L|224:144,1,144,4|2,0:3|1:2,0:0:0:0:
140,144,119123,1,0,0:0:0:0:
182,70,119357,2,0,P|194:34|190:-6,1,72,4|0,0:3|3:0,0:0:0:0:
60,120,119826,2,0,P|59:156|70:190,1,72,2|0,1:2|0:0,0:0:0:0:
128,256,120295,6,0,B|176:240|176:240|268:256,1,144,4|2,0:3|1:2,0:0:0:0:
348,224,120998,2,0,P|352:268|344:296,1,72,0|4,0:0|0:3,0:0:0:0:
268,256,121466,2,0,P|264:212|272:184,1,72,0|2,3:0|1:2,0:0:0:0:
344,136,121935,1,0,0:0:0:0:
392,64,122170,6,0,P|316:56|260:112,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
432,140,123341,1,0,3:0:0:0:
412,224,123576,2,0,L|488:228,1,72,2|0,1:2|0:0,0:0:0:0:
332,256,124045,6,0,P|263:246|208:290,1,144,4|2,0:3|1:2,0:0:0:0:
286,327,124748,1,0,0:0:0:0:
208,290,124982,2,0,L|212:218,1,72,4|0,0:3|3:0,0:0:0:0:
135,181,125451,2,0,L|124:252,1,72,2|0,1:2|0:0,0:0:0:0:
44,204,125920,6,0,L|80:56,1,144,4|2,0:3|1:2,0:0:0:0:
236,132,126857,2,0,P|304:136|372:104,1,144,4|2,0:3|1:2,0:0:0:0:
428,40,127560,1,0,0:0:0:0:
456,124,127795,6,0,L|460:196,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
344,256,128732,2,0,L|340:328,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
268,216,129435,1,0,0:0:0:0:
180,220,129670,5,4,2:0:0:0:
256,40,130373,1,2,0:0:0:0:
64,68,131076,1,2,0:0:0:0:
92,136,131310,1,0,0:0:0:0:
64,204,131545,6,0,L|60:288,1,72
31,343,132248,2,0,P|86:345|127:309,1,108
332,220,133420,5,2,0:0:0:0:
256,40,134123,1,2,0:0:0:0:
448,68,134826,1,2,0:0:0:0:
420,136,135060,1,0,0:0:0:0:
448,204,135295,6,0,L|452:288,1,72,2|0,0:0|0:0,0:0:0:0:
480,343,135998,2,0,P|426:345|385:309,1,108
256,192,137170,5,2,0:0:0:0:
156,360,137873,1,2,0:0:0:0:
356,360,138576,2,0,L|352:308,1,36,2|0,0:0|0:0,0:0:0:0:
304,268,139045,6,0,P|336:253|372:252,1,72
448,260,139748,2,0,L|444:152,1,108
256,192,140920,5,2,0:0:0:0:
356,24,141623,1,2,0:0:0:0:
156,24,142326,2,0,L|160:72,1,36,2|0,0:0|0:0,0:0:0:0:
208,116,142795,6,0,P|176:131|140:132,1,72,2|0,0:0|0:0,0:0:0:0:
64,124,143498,2,0,L|68:232,1,108
68,232,144670,5,4,0:3:0:0:
216,320,145138,1,4,0:3:0:0:
304,172,145607,1,4,0:3:0:0:
156,84,146075,1,4,0:3:0:0:
296,320,146545,5,4,0:3:0:0:
208,172,147013,1,4,0:3:0:0:
356,84,147482,1,4,0:3:0:0:
444,232,147950,1,4,0:3:0:0:
296,320,148420,6,0,P|252:328|192:296,2,108.000004119873,4|4|4,0:3|0:3|0:3,0:0:0:0:
260,248,149591,1,0,0:0:0:0:
320,196,149826,2,0,L|316:140,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
120,236,159670,6,0,L|176:232,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
160,152,160138,2,0,L|104:156,1,54.0000020599366,2|0,0:0|0:0,0:0:0:0:
240,180,160607,2,0,P|292:188|344:172,1,108.000004119873,4|2,0:3|0:0,3:0:0:0:
408,120,161310,1,0,3:0:0:0:
424,200,161545,6,0,L|420:256,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
376,320,162013,2,0,P|396:328|480:304,2,108.000004119873,2|6|2,2:0|0:3|2:0,3:0:0:0:
312,268,163185,1,0,0:0:0:0:
296,348,163420,6,0,L|240:344,1,54.0000020599366,4|0,3:0|3:0,0:0:0:0:
160,320,163888,2,0,L|100:316,1,57.6,4|0,3:0|3:0,0:0:0:0:
64,232,164357,6,0,L|128:228,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0:
204,200,164825,2,0,L|268:196,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0:
232,108,165295,6,0,L|164:104,1,68.399998173523,4|0,3:0|3:0,0:0:0:0:
80,84,165763,2,0,L|4:80,1,72,4|0,3:0|3:0,0:0:0:0:
324,120,167170,6,0,P|388:128|456:92,1,144,4|2,0:0|1:2,0:0:0:0:
496,168,167873,1,0,0:0:0:0:
496,168,168107,2,0,P|484:204|488:256,1,72,4|0,0:3|3:0,0:0:0:0:
408,296,168576,2,0,P|398:261|378:231,1,72,2|0,1:2|0:0,0:0:0:0:
296,200,169045,6,0,B|228:228|156:204,1,144,4|2,0:3|1:2,0:0:0:0:
84,156,169748,1,0,0:0:0:0:
80,244,169982,2,0,L|76:316,1,72,4|0,0:3|3:0,0:0:0:0:
170,274,170451,2,0,L|156:204,1,72,2|0,1:2|0:0,0:0:0:0:
216,140,170920,6,0,L|284:276,1,144,4|2,0:3|1:2,0:0:0:0:
320,344,171623,1,0,0:0:0:0:
372,276,171857,2,0,P|366:240|349:207,1,72,4|0,0:3|3:0,0:0:0:0:
312,132,172326,2,0,L|276:60,1,72,2|0,1:2|0:0,0:0:0:0:
208,20,172795,6,0,P|272:36|348:12,1,144,4|2,0:3|1:2,0:0:0:0:
424,48,173498,2,0,L|412:132,1,72,0|4,0:0|0:3,0:0:0:0:
484,168,173966,2,0,L|472:252,1,72,0|2,3:0|1:2,0:0:0:0:
400,280,174435,1,0,0:0:0:0:
346,348,174670,6,0,P|414:363|472:324,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
312,268,175841,1,0,3:0:0:0:
256,336,176076,2,0,L|184:332,1,72,2|0,1:2|0:0,0:0:0:0:
80,244,176545,6,0,B|140:248|140:248|164:244|164:244|223:247,1,144,4|2,0:3|1:2,0:0:0:0:
312,268,177248,1,0,0:0:0:0:
224,247,177482,2,0,P|240:215|272:187,1,72,4|0,0:3|3:0,0:0:0:0:
204,131,177951,2,0,P|233:111|275:103,1,72,2|0,1:2|0:0,0:0:0:0:
240,23,178420,6,0,B|280:15|316:35|316:35|376:71,1,144,4|2,0:3|1:2,0:0:0:0:
399,236,179357,2,0,B|359:244|323:224|323:224|263:188,1,144,4|2,0:3|1:2,0:0:0:0:
204,132,180060,1,0,0:0:0:0:
184,216,180295,6,0,L|188:288,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
120,156,180998,1,0,0:0:0:0:
56,96,181232,2,0,L|60:24,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
36,180,181935,1,0,0:0:0:0:
100,240,182170,6,0,P|144:300|116:380,2,144,4|2|4,0:0|1:2|0:3,0:0:0:0:
60,316,183341,1,0,0:0:0:0:
220,352,183576,2,0,L|308:348,1,72,2|0,1:2|0:0,0:0:0:0:
396,264,184045,6,0,B|336:268|336:268|312:264|312:264|253:267,1,144,4|2,0:3|1:2,0:0:0:0:
253,267,184748,1,0,0:0:0:0:
268,180,184982,2,0,L|339:177,1,72,4|0,0:3|0:0,0:0:0:0:
164,280,185451,2,0,L|92:282,1,72,2|0,1:2|0:0,0:0:0:0:
52,208,185920,6,0,P|8:268|32:344,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
140,212,187091,1,0,0:0:0:0:
92,284,187326,2,0,P|104:316|100:368,1,72,2|0,1:2|0:0,0:0:0:0:
52,208,187795,6,0,P|48:136|76:72,1,144,4|2,0:3|1:2,0:0:0:0:
160,52,188498,2,0,P|188:28|220:16,1,72,0|4,0:0|0:3,0:0:0:0:
232,100,188966,2,0,P|268:93|301:98,1,72,0|2,0:0|1:2,0:0:0:0:
372,152,189435,1,0,0:0:0:0:
420,224,189670,6,0,P|428:296|400:360,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
372,152,190841,1,0,0:0:0:0:
392,68,191076,2,0,L|465:64,1,72,2|0,1:2|0:0,0:0:0:0:
304,92,191545,6,0,P|236:104|168:76,1,144,4|2,0:3|1:2,0:0:0:0:
108,12,192248,1,0,0:0:0:0:
168,76,192482,2,0,L|172:152,1,72,4|0,0:3|0:0,0:0:0:0:
80,136,192951,2,0,L|101:204,1,72,2|0,1:2|0:0,0:0:0:0:
12,220,193420,6,0,B|50:279|50:279|80:300|120:292,1,144,4|2,0:3|1:2,0:0:0:0:
284,232,194357,2,0,B|320:221|352:244|352:244|384:267|420:256,1,144,4|2,0:3|1:2,0:0:0:0:
488,200,195060,1,0,0:0:0:0:
507,284,195295,6,0,P|492:315|464:338,1,72,4|0,0:0|0:0,0:0:0:0:
380,356,195763,2,0,L|236:352,1,144,0|4,1:0|0:3,0:0:0:0:
152,328,196466,1,0,3:0:0:0:
64,336,196701,2,0,P|29:325|4:300,1,72,0|0,1:0|0:0,0:0:0:0:
76,252,197170,6,0,P|108:188|96:116,1,144,4|0,0:0|1:0,0:0:0:0:
36,56,197873,1,2,0:0:0:0:
120,32,198107,2,0,L|192:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
248,152,199045,6,0,P|280:168|304:196,1,72,4|2,0:3|0:0,0:0:0:0:
336,277,199513,2,0,P|306:296|269:303,1,72,2|0,1:2|0:0,0:0:0:0:
183,290,199982,2,0,P|180:254|193:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
436,252,200920,6,0,P|404:188|416:116,1,144,4|0,0:3|1:0,0:0:0:0:
476,56,201623,1,2,0:0:0:0:
392,32,201857,2,0,L|320:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
264,152,202795,6,0,P|232:168|208:196,1,72,4|2,0:3|0:0,0:0:0:0:
176,277,203263,2,0,P|205:296|242:303,1,72,2|0,1:2|0:0,0:0:0:0:
329,290,203732,2,0,P|331:254|318:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
72,324,204670,6,0,B|60:272|60:272|76:180,1,144,4|0,0:0|1:0,0:0:0:0:
92,96,205373,1,2,0:0:0:0:
8,124,205607,2,0,P|5:88|14:53,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
168,192,206545,6,0,P|200:174|237:173,1,72,4|2,0:3|0:0,0:0:0:0:
320,160,207013,2,0,P|318:196|301:229,1,72,2|0,1:2|0:0,0:0:0:0:
272,307,207482,2,0,P|240:287|221:256,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
440,324,208420,6,0,B|452:272|452:272|436:180,1,144,4|0,0:3|1:0,0:0:0:0:
420,96,209123,1,2,0:0:0:0:
504,124,209357,2,0,P|507:88|498:53,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
344,192,210295,6,0,P|311:174|274:173,1,72,4|2,0:3|0:0,0:0:0:0:
190,156,210763,2,0,P|191:192|208:225,1,72,2|0,1:2|0:0,0:0:0:0:
288,256,211232,1,4,0:3:0:0:
132,332,211701,1,0,1:0:0:0:
28,192,212170,6,0,P|16:120|44:56,1,144,4|0,0:0|1:0,0:0:0:0:
120,16,212873,1,2,0:0:0:0:
204,32,213107,2,0,L|304:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
192,204,214045,6,0,P|196:240|216:272,1,72,4|2,0:3|0:0,0:0:0:0:
298,241,214513,2,0,P|327:219|345:186,1,72,6|0,1:2|0:0,0:0:0:0:
280,132,214982,2,0,P|246:117|209:118,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
484,192,215920,6,0,P|496:120|468:56,1,144,4|0,0:3|1:0,0:0:0:0:
392,16,216623,1,2,0:0:0:0:
308,32,216857,2,0,L|208:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
320,204,217795,6,0,P|316:240|296:272,1,72,4|2,0:3|0:0,0:0:0:0:
213,241,218263,2,0,P|184:219|166:186,1,72,2|0,1:2|0:0,0:0:0:0:
232,132,218732,2,0,B|260:112|300:116|300:116|384:128,1,144,4|0,0:3|1:0,0:0:0:0:
348,336,219670,6,0,B|320:356|280:352|280:352|196:340,1,144,4|0,0:0|1:0,0:0:0:0:
124,328,220373,1,2,0:0:0:0:
54,276,220607,2,0,P|41:308|39:345,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
156,80,221545,6,0,L|251:94,1,72,4|2,0:3|0:0,0:0:0:0:
212,169,222013,2,0,L|148:160,1,64.799998022461,2|0,1:2|0:0,0:0:0:0:
140,240,222482,2,0,L|216:252,2,57.6,4|2|0,0:3|0:0|1:0,0:0:0:0:
256,192,223420,12,0,227170,0:0:0:0:
";
} }
} }

View File

@ -2,9 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System.Linq;
namespace osu.Desktop.Tests.Visual namespace osu.Desktop.Tests.Visual
{ {
@ -12,11 +12,10 @@ namespace osu.Desktop.Tests.Visual
{ {
public override string Description => @"Testing replay playback."; public override string Description => @"Testing replay playback.";
protected override Player CreatePlayer(WorkingBeatmap beatmap) protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{ {
beatmap.Mods.Value = new Mod[] { new OsuModAutoplay() }; beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return base.CreatePlayer(beatmap, ruleset);
return base.CreatePlayer(beatmap);
} }
} }
} }

View File

@ -72,7 +72,7 @@ namespace osu.Desktop.Tests.Visual
[Test] [Test]
public void TestSpeedAdjustmentOrdering() public void TestSpeedAdjustmentOrdering()
{ {
var hitObjectContainer = new ScrollingPlayfield<TestHitObject, TestJudgement>.ScrollingHitObjectContainer(Axes.X); var hitObjectContainer = new ScrollingPlayfield.ScrollingHitObjectContainer(Axes.X);
var speedAdjustments = new[] var speedAdjustments = new[]
{ {
@ -129,7 +129,7 @@ namespace osu.Desktop.Tests.Visual
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1])); Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
} }
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject, TestJudgement> private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject>
{ {
private readonly Axes scrollingAxes; private readonly Axes scrollingAxes;
@ -147,14 +147,14 @@ namespace osu.Desktop.Tests.Visual
protected override BeatmapConverter<TestHitObject> CreateBeatmapConverter() => new TestBeatmapConverter(); protected override BeatmapConverter<TestHitObject> CreateBeatmapConverter() => new TestBeatmapConverter();
protected override Playfield<TestHitObject, TestJudgement> CreatePlayfield() => new TestPlayfield(scrollingAxes); protected override Playfield CreatePlayfield() => new TestPlayfield(scrollingAxes);
protected override DrawableHitObject<TestHitObject, TestJudgement> GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h); protected override DrawableHitObject<TestHitObject> GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
} }
private class TestScoreProcessor : ScoreProcessor<TestHitObject, TestJudgement> private class TestScoreProcessor : ScoreProcessor<TestHitObject>
{ {
protected override void OnNewJudgement(TestJudgement judgement) protected override void OnNewJudgement(Judgement judgement)
{ {
} }
} }
@ -169,7 +169,7 @@ namespace osu.Desktop.Tests.Visual
} }
} }
private class DrawableTestHitObject : DrawableScrollingHitObject<TestHitObject, TestJudgement> private class DrawableTestHitObject : DrawableScrollingHitObject<TestHitObject>
{ {
public DrawableTestHitObject(Axes scrollingAxes, TestHitObject hitObject) public DrawableTestHitObject(Axes scrollingAxes, TestHitObject hitObject)
: base(hitObject) : base(hitObject)
@ -185,14 +185,12 @@ namespace osu.Desktop.Tests.Visual
}); });
} }
protected override TestJudgement CreateJudgement() => new TestJudgement();
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
} }
} }
private class TestPlayfield : ScrollingPlayfield<TestHitObject, TestJudgement> private class TestPlayfield : ScrollingPlayfield
{ {
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content; private readonly Container<Drawable> content;
@ -218,11 +216,5 @@ namespace osu.Desktop.Tests.Visual
private class TestHitObject : HitObject private class TestHitObject : HitObject
{ {
} }
private class TestJudgement : Judgement
{
public override string ResultString { get { throw new NotImplementedException(); } }
public override string MaxResultString { get { throw new NotImplementedException(); } }
}
} }
} }

View File

@ -128,7 +128,7 @@ namespace osu.Desktop.Tests.Visual
private void addHitJudgement(bool kiai) private void addHitJudgement(bool kiai)
{ {
TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great; HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
var cpi = new ControlPointInfo(); var cpi = new ControlPointInfo();
cpi.EffectPoints.Add(new EffectControlPoint cpi.EffectPoints.Add(new EffectControlPoint
@ -139,36 +139,20 @@ namespace osu.Desktop.Tests.Visual
Hit hit = new Hit(); Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty()); hit.ApplyDefaults(cpi, new BeatmapDifficulty());
var h = new DrawableTestHit(hit) var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
{
X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f),
Judgement = new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = hitResult,
TimeOffset = 0
}
};
rulesetContainer.Playfield.OnJudgement(h); rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult });
if (RNG.Next(10) == 0) if (RNG.Next(10) == 0)
{ {
h.Judgement.SecondHit = true; rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult });
rulesetContainer.Playfield.OnJudgement(h); rulesetContainer.Playfield.OnJudgement(h, new TaikoStrongHitJudgement());
} }
} }
private void addMissJudgement() private void addMissJudgement()
{ {
rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()) rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss });
{
Judgement = new TaikoJudgement
{
Result = HitResult.Miss,
TimeOffset = 0
}
});
} }
private void addBarLine(bool major, double delay = scroll_time) private void addBarLine(bool major, double delay = scroll_time)
@ -230,15 +214,13 @@ namespace osu.Desktop.Tests.Visual
rulesetContainer.Playfield.Add(new DrawableRimHit(h)); rulesetContainer.Playfield.Add(new DrawableRimHit(h));
} }
private class DrawableTestHit : DrawableHitObject<TaikoHitObject, TaikoJudgement> private class DrawableTestHit : DrawableHitObject<TaikoHitObject>
{ {
public DrawableTestHit(TaikoHitObject hitObject) public DrawableTestHit(TaikoHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
} }

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Tests</RootNamespace> <RootNamespace>osu.Desktop.Tests</RootNamespace>
<AssemblyName>osu.Desktop.Tests</AssemblyName> <AssemblyName>osu.Desktop.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -87,6 +87,7 @@
<Compile Include="Visual\TestCaseManiaHitObjects.cs" /> <Compile Include="Visual\TestCaseManiaHitObjects.cs" />
<Compile Include="Visual\TestCaseManiaPlayfield.cs" /> <Compile Include="Visual\TestCaseManiaPlayfield.cs" />
<Compile Include="Visual\TestCaseMedalOverlay.cs" /> <Compile Include="Visual\TestCaseMedalOverlay.cs" />
<Compile Include="Visual\TestCaseEditorMenuBar.cs" />
<Compile Include="Visual\TestCaseMenuButtonSystem.cs" /> <Compile Include="Visual\TestCaseMenuButtonSystem.cs" />
<Compile Include="Visual\TestCaseMenuOverlays.cs" /> <Compile Include="Visual\TestCaseMenuOverlays.cs" />
<Compile Include="Visual\TestCaseMods.cs" /> <Compile Include="Visual\TestCaseMods.cs" />

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent> <RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone> <TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<Install>true</Install> <Install>true</Install>

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent> <RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone> <TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<Install>true</Install> <Install>true</Install>

View File

@ -93,8 +93,6 @@ namespace osu.Game.Rulesets.Catch
} }
} }
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!catch"; public override string Description => "osu!catch";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };

View File

@ -7,8 +7,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
public class CatchJudgement : Judgement public class CatchJudgement : Judgement
{ {
public override string ResultString => string.Empty; // todo: wangs
public override string MaxResultString => string.Empty;
} }
} }

View File

@ -9,14 +9,14 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
public class DrawableFruit : DrawableScrollingHitObject<CatchBaseHit, CatchJudgement> public class DrawableFruit : DrawableScrollingHitObject<CatchBaseHit>
{ {
private const float pulp_size = 30; private const float pulp_size = 30;
@ -98,14 +98,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}; };
} }
protected override CatchJudgement CreateJudgement() => new CatchJudgement();
private const float preempt = 1000; private const float preempt = 1000;
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (Judgement.TimeOffset > 0) if (timeOffset > 0)
Judgement.Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Hit : HitResult.Miss; AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss });
} }
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)

View File

@ -1,20 +1,20 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring namespace osu.Game.Rulesets.Catch.Scoring
{ {
internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit, CatchJudgement> internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit>
{ {
public CatchScoreProcessor() public CatchScoreProcessor()
{ {
} }
public CatchScoreProcessor(RulesetContainer<CatchBaseHit, CatchJudgement> rulesetContainer) public CatchScoreProcessor(RulesetContainer<CatchBaseHit> rulesetContainer)
: base(rulesetContainer) : base(rulesetContainer)
{ {
} }
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
Accuracy.Value = 1; Accuracy.Value = 1;
} }
protected override void OnNewJudgement(CatchJudgement judgement) protected override void OnNewJudgement(Judgement judgement)
{ {
} }
} }

View File

@ -2,17 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatchPlayfield : ScrollingPlayfield<CatchBaseHit, CatchJudgement> public class CatchPlayfield : ScrollingPlayfield
{ {
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content; private readonly Container<Drawable> content;
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.UI
}; };
} }
public override void Add(DrawableHitObject<CatchBaseHit, CatchJudgement> h) public override void Add(DrawableHitObject h)
{ {
base.Add(h); base.Add(h);
@ -53,13 +52,13 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.OnJudgement += Fruit_OnJudgement; fruit.OnJudgement += Fruit_OnJudgement;
} }
private void Fruit_OnJudgement(DrawableHitObject<CatchBaseHit, CatchJudgement> obj) private void Fruit_OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{ {
if (obj.Judgement.Result == HitResult.Hit) if (judgement.IsHit)
{ {
Vector2 screenPosition = obj.ScreenSpaceDrawQuad.Centre; Vector2 screenPosition = judgedObject.ScreenSpaceDrawQuad.Centre;
Remove(obj); Remove(judgedObject);
catcherArea.Add(obj, screenPosition); catcherArea.Add(judgedObject, screenPosition);
} }
} }
} }

View File

@ -5,7 +5,6 @@ using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Scoring;
@ -15,7 +14,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchBaseHit, CatchJudgement> public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchBaseHit>
{ {
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap, isForCurrentRuleset)
@ -26,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter(); protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override Playfield<CatchBaseHit, CatchJudgement> CreatePlayfield() => new CatchPlayfield(); protected override Playfield CreatePlayfield() => new CatchPlayfield();
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
protected override DrawableHitObject<CatchBaseHit, CatchJudgement> GetVisualRepresentation(CatchBaseHit h) protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h)
{ {
if (h is Fruit) if (h is Fruit)
return new DrawableFruit(h); return new DrawableFruit(h);

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -22,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
private Catcher catcher; private Catcher catcher;
public void Add(DrawableHitObject<CatchBaseHit, CatchJudgement> fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition); public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition);
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.Position) < catcher.DrawSize.X / DrawSize.X / 2; public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.Position) < catcher.DrawSize.X / DrawSize.X / 2;
@ -85,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.UI
var additive = createCatcherSprite(); var additive = createCatcherSprite();
additive.RelativePositionAxes = Axes.Both; additive.RelativePositionAxes = Axes.Both;
additive.BlendingMode = BlendingMode.Additive; additive.Blending = BlendingMode.Additive;
additive.Position = Position; additive.Position = Position;
additive.Scale = Scale; additive.Scale = Scale;
@ -152,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.UI
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime / 1800 * speed, 0, 1); X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime / 1800 * speed, 0, 1);
} }
public void AddToStack(DrawableHitObject<CatchBaseHit, CatchJudgement> fruit, Vector2 absolutePosition) public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition)
{ {
fruit.RelativePositionAxes = Axes.None; fruit.RelativePositionAxes = Axes.None;
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0); fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
@ -161,7 +160,6 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.Origin = Anchor.BottomCentre; fruit.Origin = Anchor.BottomCentre;
fruit.Scale *= 0.7f; fruit.Scale *= 0.7f;
fruit.LifetimeEnd = double.MaxValue; fruit.LifetimeEnd = double.MaxValue;
fruit.Depth = (float)Time.Current;
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X; float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Catch</RootNamespace> <RootNamespace>osu.Game.Rulesets.Catch</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch</AssemblyName> <AssemblyName>osu.Game.Rulesets.Catch</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements namespace osu.Game.Rulesets.Mania.Judgements
{ {
@ -145,18 +146,18 @@ namespace osu.Game.Rulesets.Mania.Judgements
/// </summary> /// </summary>
/// <param name="hitOffset">The time offset.</param> /// <param name="hitOffset">The time offset.</param>
/// <returns>The hit result, or null if the time offset results in a miss.</returns> /// <returns>The hit result, or null if the time offset results in a miss.</returns>
public ManiaHitResult? ResultFor(double hitOffset) public HitResult? ResultFor(double hitOffset)
{ {
if (hitOffset <= Perfect / 2) if (hitOffset <= Perfect / 2)
return ManiaHitResult.Perfect; return HitResult.Perfect;
if (hitOffset <= Great / 2) if (hitOffset <= Great / 2)
return ManiaHitResult.Great; return HitResult.Great;
if (hitOffset <= Good / 2) if (hitOffset <= Good / 2)
return ManiaHitResult.Good; return HitResult.Good;
if (hitOffset <= Ok / 2) if (hitOffset <= Ok / 2)
return ManiaHitResult.Ok; return HitResult.Ok;
if (hitOffset <= Bad / 2) if (hitOffset <= Bad / 2)
return ManiaHitResult.Bad; return HitResult.Meh;
return null; return null;
} }

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements namespace osu.Game.Rulesets.Mania.Judgements
{ {
public class HoldNoteTailJudgement : ManiaJudgement public class HoldNoteTailJudgement : ManiaJudgement
@ -10,27 +12,27 @@ namespace osu.Game.Rulesets.Mania.Judgements
/// </summary> /// </summary>
public bool HasBroken; public bool HasBroken;
public override int NumericResultForScore(ManiaHitResult result) protected override int NumericResultFor(HitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return base.NumericResultForScore(result); return base.NumericResultFor(result);
case ManiaHitResult.Great: case HitResult.Great:
case ManiaHitResult.Perfect: case HitResult.Perfect:
return base.NumericResultForScore(HasBroken ? ManiaHitResult.Good : result); return base.NumericResultFor(HasBroken ? HitResult.Good : result);
} }
} }
public override int NumericResultForAccuracy(ManiaHitResult result) protected override int NumericResultForAccuracy(HitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return base.NumericResultForAccuracy(result); return base.NumericResultForAccuracy(result);
case ManiaHitResult.Great: case HitResult.Great:
case ManiaHitResult.Perfect: case HitResult.Perfect:
return base.NumericResultForAccuracy(HasBroken ? ManiaHitResult.Good : result); return base.NumericResultForAccuracy(HasBroken ? HitResult.Good : result);
} }
} }
} }

View File

@ -1,13 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements namespace osu.Game.Rulesets.Mania.Judgements
{ {
public class HoldNoteTickJudgement : ManiaJudgement public class HoldNoteTickJudgement : ManiaJudgement
{ {
public override bool AffectsCombo => false; public override bool AffectsCombo => false;
public override int NumericResultForScore(ManiaHitResult result) => 20; protected override int NumericResultFor(HitResult result) => 20;
public override int NumericResultForAccuracy(ManiaHitResult result) => 0; // Don't count ticks into accuracy protected override int NumericResultForAccuracy(HitResult result) => 0; // Don't count ticks into accuracy
} }
} }

View File

@ -1,21 +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.ComponentModel;
namespace osu.Game.Rulesets.Mania.Judgements
{
public enum ManiaHitResult
{
[Description("PERFECT")]
Perfect,
[Description("GREAT")]
Great,
[Description("GOOD")]
Good,
[Description("OK")]
Ok,
[Description("BAD")]
Bad
}
}

View File

@ -8,73 +8,49 @@ namespace osu.Game.Rulesets.Mania.Judgements
{ {
public class ManiaJudgement : Judgement public class ManiaJudgement : Judgement
{ {
/// <summary>
/// The maximum possible hit result.
/// </summary>
public const ManiaHitResult MAX_HIT_RESULT = ManiaHitResult.Perfect;
/// <summary>
/// The result value for the combo portion of the score.
/// </summary>
public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(ManiaResult);
/// <summary>
/// The result value for the accuracy portion of the score.
/// </summary>
public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(ManiaResult);
/// <summary>
/// The maximum result value for the combo portion of the score.
/// </summary>
public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
/// <summary> /// <summary>
/// The maximum result value for the accuracy portion of the score. /// The maximum result value for the accuracy portion of the score.
/// </summary> /// </summary>
public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT); public int MaxNumericAccuracyResult => NumericResultForAccuracy(HitResult.Perfect);
public override string ResultString => string.Empty; protected override int NumericResultFor(HitResult result)
public override string MaxResultString => string.Empty;
/// <summary>
/// The hit result.
/// </summary>
public ManiaHitResult ManiaResult;
public virtual int NumericResultForScore(ManiaHitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return 0; return 0;
case ManiaHitResult.Bad: case HitResult.Meh:
return 50; return 50;
case ManiaHitResult.Ok: case HitResult.Ok:
return 100; return 100;
case ManiaHitResult.Good: case HitResult.Good:
return 200; return 200;
case ManiaHitResult.Great: case HitResult.Great:
case ManiaHitResult.Perfect: case HitResult.Perfect:
return 300; return 300;
} }
} }
public virtual int NumericResultForAccuracy(ManiaHitResult result) public int NumericAccuracyResult => NumericResultForAccuracy(Result);
/// <summary>
/// The result value for the accuracy portion of the score.
/// </summary>
protected virtual int NumericResultForAccuracy(HitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return 0; return 0;
case ManiaHitResult.Bad: case HitResult.Meh:
return 50; return 50;
case ManiaHitResult.Ok: case HitResult.Ok:
return 100; return 100;
case ManiaHitResult.Good: case HitResult.Good:
return 200; return 200;
case ManiaHitResult.Great: case HitResult.Great:
return 300; return 300;
case ManiaHitResult.Perfect: case HitResult.Perfect:
return 305; return 305;
} }
} }

View File

@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Mania
{ {
Mods = new Mod[] Mods = new Mod[]
{ {
new ModAutoplay(), new ManiaModAutoplay(),
new ModCinema(), new ModCinema(),
}, },
}, },
@ -105,8 +105,6 @@ namespace osu.Game.Rulesets.Mania
} }
} }
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!mania"; public override string Description => "osu!mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };

View File

@ -4,6 +4,13 @@
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using System; using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
@ -68,6 +75,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModFadeIn : Mod public class ManiaModFadeIn : Mod
{ {
public override string Name => "FadeIn"; public override string Name => "FadeIn";
public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden; public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease; public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
@ -78,12 +86,14 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModRandom : Mod public class ManiaModRandom : Mod
{ {
public override string Name => "Random"; public override string Name => "Random";
public override string ShortenedName => "RD";
public override string Description => @"Shuffle around the notes!"; public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
} }
public abstract class ManiaKeyMod : Mod public abstract class ManiaKeyMod : Mod
{ {
public override string ShortenedName => Name;
public abstract int KeyCount { get; } public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true; public override bool Ranked => true;
@ -146,8 +156,29 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKeyCoop : Mod public class ManiaModKeyCoop : Mod
{ {
public override string Name => "KeyCoop"; public override string Name => "KeyCoop";
public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!"; public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
public override bool Ranked => true; public override bool Ranked => true;
} }
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
{
private int availableColumns;
public override void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
// Todo: This shouldn't be done, we should be getting a ManiaBeatmap which should store AvailableColumns
// But this is dependent on a _lot_ of refactoring
var maniaRulesetContainer = (ManiaRulesetContainer)rulesetContainer;
availableColumns = maniaRulesetContainer.AvailableColumns;
base.ApplyToRulesetContainer(rulesetContainer);
}
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
{
User = new User { Username = "osu!topus!" },
Replay = new ManiaAutoGenerator(beatmap, availableColumns).Generate(),
};
}
} }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
{ {
public override string Name => "Gravity"; public override string Name => "Gravity";
public override string ShortenedName => "GR";
public override double ScoreMultiplier => 0; public override double ScoreMultiplier => 0;
@ -23,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges) public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges)
{ {
// We have to generate one speed adjustment per hit object for gravity // We have to generate one speed adjustment per hit object for gravity
foreach (ManiaHitObject obj in rulesetContainer.Objects) foreach (ManiaHitObject obj in rulesetContainer.Objects.OfType<ManiaHitObject>())
{ {
MultiplierControlPoint controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime); MultiplierControlPoint controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime);
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now // Beat length has too large of an effect for gravity, so we'll force it to a constant value for now

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
@ -21,8 +23,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private readonly DrawableNote head; private readonly DrawableNote head;
private readonly DrawableNote tail; private readonly DrawableNote tail;
private readonly GlowPiece glowPiece;
private readonly BodyPiece bodyPiece; private readonly BodyPiece bodyPiece;
private readonly Container<DrawableHoldNoteTick> tickContainer; private readonly Container<DrawableHoldNoteTick> tickContainer;
private readonly Container fullHeightContainer;
/// <summary> /// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note. /// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
@ -42,13 +46,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddRange(new Drawable[] AddRange(new Drawable[]
{ {
// For now the body piece covers the entire height of the container // The hit object itself cannot be used for various elements because the tail overshoots it
// whereas possibly in the future we don't want to extend under the head/tail. // So a specialized container that is updated to contain the tail height is used
// This will be fixed when new designs are given or the current design is finalized. fullHeightContainer = new Container
{
RelativeSizeAxes = Axes.X,
Child = glowPiece = new GlowPiece()
},
bodyPiece = new BodyPiece bodyPiece = new BodyPiece
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
}, },
tickContainer = new Container<DrawableHoldNoteTick> tickContainer = new Container<DrawableHoldNoteTick>
{ {
@ -94,6 +103,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
tickContainer.Children.ForEach(t => t.AccentColour = value); tickContainer.Children.ForEach(t => t.AccentColour = value);
glowPiece.AccentColour = value;
bodyPiece.AccentColour = value; bodyPiece.AccentColour = value;
head.AccentColour = value; head.AccentColour = value;
tail.AccentColour = value; tail.AccentColour = value;
@ -104,6 +114,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
} }
protected override void Update()
{
base.Update();
// Make the body piece not lie under the head note
bodyPiece.Y = head.Height;
bodyPiece.Height = DrawHeight - head.Height;
// Make the fullHeightContainer "contain" the height of the tail note, keeping in mind
// that the tail note overshoots the height of this hit object
fullHeightContainer.Height = DrawHeight + tail.Height;
}
public bool OnPressed(ManiaAction action) public bool OnPressed(ManiaAction action)
{ {
// Make sure the action happened within the body of the hold note // Make sure the action happened within the body of the hold note
@ -133,7 +156,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
holdStartTime = null; holdStartTime = null;
// If the key has been released too early, the user should not receive full score for the release // If the key has been released too early, the user should not receive full score for the release
if (!tail.Judged) if (!tail.AllJudged)
hasBroken = true; hasBroken = true;
return true; return true;
@ -153,6 +176,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativePositionAxes = Axes.None; RelativePositionAxes = Axes.None;
Y = 0; Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
GlowPiece.Alpha = 0;
} }
public override bool OnPressed(ManiaAction action) public override bool OnPressed(ManiaAction action)
@ -160,12 +189,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!base.OnPressed(action)) if (!base.OnPressed(action))
return false; return false;
// We only want to trigger a holding state from the head if the head has received a judgement
if (!Judged)
return false;
// If the key has been released too early, the user should not receive full score for the release // If the key has been released too early, the user should not receive full score for the release
if (Judgement.Result == HitResult.Miss) if (Judgements.Any(j => j.Result == HitResult.Miss))
holdNote.hasBroken = true; holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held // The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
@ -190,19 +215,40 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativePositionAxes = Axes.None; RelativePositionAxes = Axes.None;
Y = 0; Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
GlowPiece.Alpha = 0;
} }
protected override ManiaJudgement CreateJudgement() => new HoldNoteTailJudgement(); protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckJudgement(bool userTriggered)
{ {
base.CheckJudgement(userTriggered); if (!userTriggered)
{
if (timeOffset > HitObject.HitWindows.Bad / 2)
{
AddJudgement(new HoldNoteTailJudgement
{
Result = HitResult.Miss,
HasBroken = holdNote.hasBroken
});
}
var tailJudgement = Judgement as HoldNoteTailJudgement; return;
if (tailJudgement == null) }
double offset = Math.Abs(timeOffset);
if (offset > HitObject.HitWindows.Miss / 2)
return; return;
tailJudgement.HasBroken = holdNote.hasBroken; AddJudgement(new HoldNoteTailJudgement
{
Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss,
HasBroken = holdNote.hasBroken
});
} }
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
@ -213,9 +259,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!holdNote.holdStartTime.HasValue) if (!holdNote.holdStartTime.HasValue)
return false; return false;
if (Judgement.Result != HitResult.None)
return false;
if (action != Action) if (action != Action)
return false; return false;

View File

@ -23,11 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary> /// </summary>
public Func<double?> HoldStartTime; public Func<double?> HoldStartTime;
/// <summary>
/// References whether the user is currently holding the hold note.
/// </summary>
public Func<bool> IsHolding;
private readonly Container glowContainer; private readonly Container glowContainer;
public DrawableHoldNoteTick(HoldNoteTick hitObject) public DrawableHoldNoteTick(HoldNoteTick hitObject)
@ -36,9 +31,15 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
Y = (float)HitObject.StartTime;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Size = new Vector2(1); Size = new Vector2(1);
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
Children = new[] Children = new[]
{ {
glowContainer = new CircularContainer glowContainer = new CircularContainer
@ -58,9 +59,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
} }
} }
}; };
// Set the default glow
AccentColour = Color4.White;
} }
public override Color4 AccentColour public override Color4 AccentColour
@ -80,9 +78,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
} }
} }
protected override ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement(); protected ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement();
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (!userTriggered) if (!userTriggered)
return; return;
@ -93,8 +91,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (HoldStartTime?.Invoke() > HitObject.StartTime) if (HoldStartTime?.Invoke() > HitObject.StartTime)
return; return;
Judgement.ManiaResult = ManiaHitResult.Perfect; AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
Judgement.Result = HitResult.Hit;
} }
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
@ -109,10 +106,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void Update() protected override void Update()
{ {
if (Judgement.Result != HitResult.None) if (AllJudged)
return; return;
if (IsHolding?.Invoke() != true) if (HoldStartTime?.Invoke() == null)
return; return;
UpdateJudgement(true); UpdateJudgement(true);

View File

@ -1,13 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
public abstract class DrawableManiaHitObject<TObject> : DrawableScrollingHitObject<ManiaHitObject, ManiaJudgement> public abstract class DrawableManiaHitObject<TObject> : DrawableScrollingHitObject<ManiaHitObject>
where TObject : ManiaHitObject where TObject : ManiaHitObject
{ {
/// <summary> /// <summary>
@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null) protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
: base(hitObject) : base(hitObject)
{ {
RelativePositionAxes = Axes.Y;
HitObject = hitObject; HitObject = hitObject;
if (action != null) if (action != null)
@ -36,7 +37,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value; base.AccentColour = value;
} }
} }
protected override ManiaJudgement CreateJudgement() => new ManiaJudgement();
} }
} }

View File

@ -16,19 +16,31 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary> /// </summary>
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction> public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
{ {
protected readonly GlowPiece GlowPiece;
private readonly LaneGlowPiece laneGlowPiece;
private readonly NotePiece headPiece; private readonly NotePiece headPiece;
public DrawableNote(Note hitObject, ManiaAction action) public DrawableNote(Note hitObject, ManiaAction action)
: base(hitObject, action) : base(hitObject, action)
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 100; AutoSizeAxes = Axes.Y;
Add(headPiece = new NotePiece Children = new Drawable[]
{ {
Anchor = Anchor.TopCentre, laneGlowPiece = new LaneGlowPiece
Origin = Anchor.TopCentre {
}); Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
GlowPiece = new GlowPiece(),
headPiece = new NotePiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
};
} }
public override Color4 AccentColour public override Color4 AccentColour
@ -40,43 +52,31 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return; return;
base.AccentColour = value; base.AccentColour = value;
laneGlowPiece.AccentColour = value;
GlowPiece.AccentColour = value;
headPiece.AccentColour = value; headPiece.AccentColour = value;
} }
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > HitObject.HitWindows.Bad / 2) if (timeOffset > HitObject.HitWindows.Bad / 2)
Judgement.Result = HitResult.Miss; AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
return; return;
} }
double offset = Math.Abs(Judgement.TimeOffset); double offset = Math.Abs(timeOffset);
if (offset > HitObject.HitWindows.Miss / 2) if (offset > HitObject.HitWindows.Miss / 2)
return; return;
ManiaHitResult? tmpResult = HitObject.HitWindows.ResultFor(offset); AddJudgement(new ManiaJudgement { Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss });
if (tmpResult.HasValue)
{
Judgement.Result = HitResult.Hit;
Judgement.ManiaResult = tmpResult.Value;
}
else
Judgement.Result = HitResult.Miss;
} }
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
switch (State)
{
case ArmedState.Hit:
Colour = Color4.Green;
break;
}
} }
public virtual bool OnPressed(ManiaAction action) public virtual bool OnPressed(ManiaAction action)

View File

@ -1,7 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Caching;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -14,22 +17,61 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
/// </summary> /// </summary>
internal class BodyPiece : Container, IHasAccentColour internal class BodyPiece : Container, IHasAccentColour
{ {
private readonly Box box; private readonly Container subtractionLayer;
private readonly Drawable background;
private readonly BufferedContainer foreground;
private readonly BufferedContainer subtractionContainer;
public BodyPiece() public BodyPiece()
{ {
RelativeSizeAxes = Axes.Both; Blending = BlendingMode.Additive;
Children = new[] Children = new[]
{ {
box = new Box background = new Box { RelativeSizeAxes = Axes.Both },
foreground = new BufferedContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Alpha = 0.3f CacheDrawnFrameBuffer = true,
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both },
subtractionContainer = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
// This is needed because we're blending with another object
BackgroundColour = Color4.White.Opacity(0),
CacheDrawnFrameBuffer = true,
// The 'hole' is achieved by subtracting the result of this container with the parent
Blending = new BlendingParameters { AlphaEquation = BlendingEquation.ReverseSubtract },
Child = subtractionLayer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
// Height computed in Update
Width = 1,
Masking = true,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
} }
}; };
} }
protected override void LoadComplete()
{
base.LoadComplete();
updateAccentColour();
}
private Color4 accentColour; private Color4 accentColour;
public Color4 AccentColour public Color4 AccentColour
{ {
@ -40,8 +82,51 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
return; return;
accentColour = value; accentColour = value;
box.Colour = accentColour; updateAccentColour();
} }
} }
private Cached subtractionCache = new Cached();
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
subtractionCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update()
{
base.Update();
if (!subtractionCache.IsValid)
{
subtractionLayer.Width = 5;
subtractionLayer.Height = Math.Max(0, DrawHeight - DrawWidth);
subtractionLayer.EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.White,
Type = EdgeEffectType.Glow,
Radius = DrawWidth
};
foreground.ForceRedraw();
subtractionContainer.ForceRedraw();
subtractionCache.Validate();
}
}
private void updateAccentColour()
{
if (!IsLoaded)
return;
foreground.Colour = AccentColour.Opacity(0.4f);
background.Colour = AccentColour.Opacity(0.2f);
subtractionCache.Invalidate();
}
} }
} }

View File

@ -0,0 +1,65 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class GlowPiece : CompositeDrawable, IHasAccentColour
{
private const float glow_alpha = 0.7f;
private const float glow_radius = 5;
public GlowPiece()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateGlow();
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
updateGlow();
}
}
private void updateGlow()
{
if (!IsLoaded)
return;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Opacity(glow_alpha),
Radius = glow_radius,
Hollow = true
};
}
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class LaneGlowPiece : CompositeDrawable, IHasAccentColour
{
private const float total_height = 100;
private const float glow_height = 50;
private const float glow_alpha = 0.4f;
private const float edge_alpha = 0.3f;
public LaneGlowPiece()
{
BypassAutoSizeAxes = Axes.Both;
RelativeSizeAxes = Axes.X;
Height = total_height;
InternalChildren = new[]
{
new Container
{
Name = "Left edge",
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Right edge",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Glow",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = glow_height,
Children = createGradient(glow_alpha)
}
};
}
private Drawable[] createGradient(float alpha) => new Drawable[]
{
new Box
{
Name = "Top",
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingMode.Additive,
Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha))
},
new Box
{
Name = "Bottom",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingMode.Additive,
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent)
}
};
public Color4 AccentColour
{
get { return Colour; }
set { Colour = value; }
}
}
}

View File

@ -37,6 +37,17 @@ namespace osu.Game.Rulesets.Mania.Objects
} }
} }
public override int Column
{
get { return base.Column; }
set
{
base.Column = value;
Head.Column = value;
Tail.Column = value;
}
}
/// <summary> /// <summary>
/// The head note of the hold. /// The head note of the hold.
/// </summary> /// </summary>
@ -80,7 +91,8 @@ namespace osu.Game.Rulesets.Mania.Objects
{ {
ret.Add(new HoldNoteTick ret.Add(new HoldNoteTick
{ {
StartTime = t StartTime = t,
Column = Column
}); });
} }

View File

@ -8,6 +8,6 @@ namespace osu.Game.Rulesets.Mania.Objects
{ {
public abstract class ManiaHitObject : HitObject, IHasColumn public abstract class ManiaHitObject : HitObject, IHasColumn
{ {
public int Column { get; set; } public virtual int Column { get; set; }
} }
} }

View File

@ -0,0 +1,133 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaAutoGenerator : AutoGenerator<ManiaHitObject>
{
private const double release_delay = 20;
private readonly int availableColumns;
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap, int availableColumns)
: base(beatmap)
{
this.availableColumns = availableColumns;
Replay = new Replay { User = new User { Username = @"Autoplay" } };
}
protected Replay Replay;
public override Replay Generate()
{
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
double[] holdEndTimes = new double[availableColumns];
for (int i = 0; i < availableColumns; i++)
holdEndTimes[i] = double.NegativeInfinity;
// Notes are handled row-by-row
foreach (var objGroup in Beatmap.HitObjects.GroupBy(h => h.StartTime))
{
double groupTime = objGroup.Key;
int activeColumns = 0;
// Get the previously held-down active columns
for (int i = 0; i < availableColumns; i++)
{
if (holdEndTimes[i] > groupTime)
activeColumns |= 1 << i;
}
// Add on the group columns, keeping track of the held notes for the next rows
foreach (var obj in objGroup)
{
var holdNote = obj as HoldNote;
if (holdNote != null)
holdEndTimes[obj.Column] = Math.Max(holdEndTimes[obj.Column], holdNote.EndTime);
activeColumns |= 1 << obj.Column;
}
Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None));
// Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated
foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key))
{
var groupEndTime = obj.Key;
int activeColumnsAtEnd = 0;
for (int i = 0; i < availableColumns; i++)
{
if (holdEndTimes[i] > groupEndTime)
activeColumnsAtEnd |= 1 << i;
}
Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None));
}
}
Replay.Frames = Replay.Frames
// Pick the maximum activeColumns for all frames at the same time
.GroupBy(f => f.Time)
.Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None))
// The addition of release frames above maybe result in unordered frames, but we need them ordered
.OrderBy(f => f.Time)
.ToList();
return Replay;
}
/// <summary>
/// Finds the maximum <see cref="ReplayFrame.MouseX"/> by count of bits from a grouping of <see cref="ReplayFrame"/>s.
/// </summary>
/// <param name="group">The <see cref="ReplayFrame"/> grouping to search.</param>
/// <returns>The maximum <see cref="ReplayFrame.MouseX"/> by count of bits.</returns>
private float maxMouseX(IGrouping<double, ReplayFrame> group)
{
int currentCount = -1;
int currentMax = 0;
foreach (var val in group)
{
int newCount = countBits((int)(val.MouseX ?? 0));
if (newCount > currentCount)
{
currentCount = newCount;
currentMax = (int)(val.MouseX ?? 0);
}
}
return currentMax;
}
/// <summary>
/// Counts the number of bits set in a value.
/// </summary>
/// <param name="value">The value to count.</param>
/// <returns>The number of set bits.</returns>
private int countBits(int value)
{
int count = 0;
while (value > 0)
{
if ((value & 1) > 0)
count++;
value >>= 1;
}
return count;
}
}
}

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 System.Collections.Generic;
using osu.Framework.Input;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
{
public ManiaFramedReplayInputHandler(Replay replay)
: base(replay)
{
}
public override List<InputState> GetPendingStates()
{
var actions = new List<ManiaAction>();
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
int counter = 0;
while (activeColumns > 0)
{
if ((activeColumns & 1) > 0)
actions.Add(ManiaAction.Key1 + counter);
counter++;
activeColumns >>= 1;
}
return new List<InputState> { new ReplayState<ManiaAction> { PressedActions = actions } };
}
}
}

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -12,7 +13,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring namespace osu.Game.Rulesets.Mania.Scoring
{ {
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject, ManiaJudgement> internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject>
{ {
/// <summary> /// <summary>
/// The maximum score achievable. /// The maximum score achievable.
@ -155,7 +156,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
{ {
} }
public ManiaScoreProcessor(RulesetContainer<ManiaHitObject, ManiaJudgement> rulesetContainer) public ManiaScoreProcessor(RulesetContainer<ManiaHitObject> rulesetContainer)
: base(rulesetContainer) : base(rulesetContainer)
{ {
} }
@ -174,37 +175,19 @@ namespace osu.Game.Rulesets.Mania.Scoring
if (obj is Note) if (obj is Note)
{ {
AddJudgement(new ManiaJudgement AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
{
Result = HitResult.Hit,
ManiaResult = ManiaHitResult.Perfect
});
} }
else if (holdNote != null) else if (holdNote != null)
{ {
// Head // Head
AddJudgement(new ManiaJudgement AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
{
Result = HitResult.Hit,
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
});
// Ticks // Ticks
int tickCount = holdNote.Ticks.Count(); int tickCount = holdNote.Ticks.Count();
for (int i = 0; i < tickCount; i++) for (int i = 0; i < tickCount; i++)
{ AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
AddJudgement(new HoldNoteTickJudgement
{
Result = HitResult.Hit,
ManiaResult = ManiaJudgement.MAX_HIT_RESULT,
});
}
AddJudgement(new HoldNoteTailJudgement AddJudgement(new HoldNoteTailJudgement { Result = HitResult.Perfect });
{
Result = HitResult.Hit,
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
});
} }
} }
@ -221,50 +204,50 @@ namespace osu.Game.Rulesets.Mania.Scoring
maxComboPortion = comboPortion; maxComboPortion = comboPortion;
} }
protected override void OnNewJudgement(ManiaJudgement judgement) protected override void OnNewJudgement(Judgement judgement)
{ {
bool isTick = judgement is HoldNoteTickJudgement; bool isTick = judgement is HoldNoteTickJudgement;
if (!isTick) if (isTick)
{
if (judgement.IsHit)
{
Health.Value += hpMultiplier * hp_increase_tick;
bonusScore += judgement.NumericResult;
}
}
else
{
totalHits++; totalHits++;
switch (judgement.Result) switch (judgement.Result)
{ {
case HitResult.Miss: case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss; Health.Value += hpMissMultiplier * hp_increase_miss;
break; break;
case HitResult.Hit: case HitResult.Meh:
if (isTick) Health.Value += hpMultiplier * hp_increase_bad;
{ break;
Health.Value += hpMultiplier * hp_increase_tick; case HitResult.Ok:
bonusScore += judgement.ResultValueForScore; Health.Value += hpMultiplier * hp_increase_ok;
} break;
else case HitResult.Good:
{ Health.Value += hpMultiplier * hp_increase_good;
switch (judgement.ManiaResult) break;
{ case HitResult.Great:
case ManiaHitResult.Bad: Health.Value += hpMultiplier * hp_increase_great;
Health.Value += hpMultiplier * hp_increase_bad; break;
break; case HitResult.Perfect:
case ManiaHitResult.Ok: Health.Value += hpMultiplier * hp_increase_perfect;
Health.Value += hpMultiplier * hp_increase_ok; break;
break; }
case ManiaHitResult.Good:
Health.Value += hpMultiplier * hp_increase_good;
break;
case ManiaHitResult.Great:
Health.Value += hpMultiplier * hp_increase_great;
break;
case ManiaHitResult.Perfect:
Health.Value += hpMultiplier * hp_increase_perfect;
break;
}
// A factor that is applied to make higher combos more relevant if (judgement.IsHit)
double comboRelevance = Math.Min(Math.Max(0.5, Math.Log(Combo.Value, combo_base)), Math.Log(combo_relevance_cap, combo_base)); {
comboPortion += judgement.ResultValueForScore * comboRelevance; // A factor that is applied to make higher combos more relevant
} double comboRelevance = Math.Min(Math.Max(0.5, Math.Log(Combo.Value, combo_base)), Math.Log(combo_relevance_cap, combo_base));
break; comboPortion += judgement.NumericResult * comboRelevance;
}
} }
int scoreForAccuracy = 0; int scoreForAccuracy = 0;
@ -272,8 +255,10 @@ namespace osu.Game.Rulesets.Mania.Scoring
foreach (var j in Judgements) foreach (var j in Judgements)
{ {
scoreForAccuracy += j.ResultValueForAccuracy; var maniaJudgement = (ManiaJudgement)j;
maxScoreForAccuracy += j.MaxResultValueForAccuracy;
scoreForAccuracy += maniaJudgement.NumericAccuracyResult;
maxScoreForAccuracy += maniaJudgement.MaxNumericAccuracyResult;
} }
Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy; Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
@ -285,6 +270,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
base.Reset(); base.Reset();
Health.Value = 1; Health.Value = 1;
Accuracy.Value = 1;
bonusScore = 0; bonusScore = 0;
comboPortion = 0; comboPortion = 0;

View File

@ -13,12 +13,11 @@ using osu.Game.Rulesets.Objects.Drawables;
using System; using System;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
public class Column : ScrollingPlayfield<ManiaHitObject, ManiaJudgement>, IHasAccentColour public class Column : ScrollingPlayfield, IHasAccentColour
{ {
private const float key_icon_size = 10; private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3; private const float key_icon_corner_radius = 3;
@ -36,9 +35,15 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Container hitTargetBar; private readonly Container hitTargetBar;
private readonly Container keyIcon; private readonly Container keyIcon;
internal readonly Container TopLevelContainer;
private readonly Container explosionContainer;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content; private readonly Container<Drawable> content;
private const float opacity_released = 0.1f;
private const float opacity_pressed = 0.25f;
public Column() public Column()
: base(Axes.Y) : base(Axes.Y)
{ {
@ -48,9 +53,9 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
background = new Box background = new Box
{ {
Name = "Foreground", Name = "Background",
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Alpha = 0.2f Alpha = opacity_released
}, },
new Container new Container
{ {
@ -98,6 +103,11 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
Pressed = onPressed, Pressed = onPressed,
Released = onReleased Released = onReleased
},
explosionContainer = new Container
{
Name = "Hit explosions",
RelativeSizeAxes = Axes.Both
} }
} }
}, },
@ -136,8 +146,11 @@ namespace osu.Game.Rulesets.Mania.UI
} }
} }
} }
} },
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
}; };
TopLevelContainer.Add(explosionContainer.CreateProxy());
} }
public override Axes RelativeSizeAxes => Axes.Y; public override Axes RelativeSizeAxes => Axes.Y;
@ -188,17 +201,25 @@ namespace osu.Game.Rulesets.Mania.UI
/// Adds a DrawableHitObject to this Playfield. /// Adds a DrawableHitObject to this Playfield.
/// </summary> /// </summary>
/// <param name="hitObject">The DrawableHitObject to add.</param> /// <param name="hitObject">The DrawableHitObject to add.</param>
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject) public override void Add(DrawableHitObject hitObject)
{ {
hitObject.AccentColour = AccentColour; hitObject.AccentColour = AccentColour;
HitObjects.Add(hitObject); HitObjects.Add(hitObject);
} }
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (!judgement.IsHit)
return;
explosionContainer.Add(new HitExplosion(judgedObject));
}
private bool onPressed(ManiaAction action) private bool onPressed(ManiaAction action)
{ {
if (action == Action) if (action == Action)
{ {
background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint); background.FadeTo(opacity_pressed, 50, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint); keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
} }
@ -209,7 +230,7 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
if (action == Action) if (action == Action)
{ {
background.FadeTo(0.2f, 800, Easing.OutQuart); background.FadeTo(opacity_released, 800, Easing.OutQuart);
keyIcon.ScaleTo(1f, 400, Easing.OutQuart); keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
} }

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
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Mania.UI
{
internal class DrawableManiaJudgement : DrawableJudgement
{
public DrawableManiaJudgement(Judgement judgement)
: base(judgement)
{
JudgementText.TextSize = 25;
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(50, Easing.OutQuint);
if (Judgement.IsHit)
{
this.ScaleTo(0.8f);
this.ScaleTo(1, 250, Easing.OutElastic);
this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250);
}
Expire();
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
internal class HitExplosion : CompositeDrawable
{
private readonly Box inner;
public HitExplosion(DrawableHitObject judgedObject)
{
bool isTick = judgedObject is DrawableHoldNoteTick;
Anchor = Anchor.TopCentre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Size = new Vector2(isTick ? 0.5f : 1);
FillMode = FillMode.Fit;
Blending = BlendingMode.Additive;
Color4 accent = isTick ? Color4.White : judgedObject.AccentColour;
InternalChild = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 1,
BorderColour = accent,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = accent,
Radius = 10,
Hollow = true
},
Child = inner = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = accent,
Alpha = 1,
AlwaysPresent = true,
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500).Expire();
inner.FadeOut(250);
}
}
}

View File

@ -6,31 +6,24 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using System; using System;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using OpenTK.Input;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
public class ManiaPlayfield : ScrollingPlayfield<ManiaHitObject, ManiaJudgement> public class ManiaPlayfield : ScrollingPlayfield
{ {
public const float HIT_TARGET_POSITION = 50; public const float HIT_TARGET_POSITION = 50;
/// <summary>
/// Default column keys, expanding outwards from the middle as more column are added.
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
/// </summary>
private static readonly Key[] default_keys = { Key.A, Key.S, Key.D, Key.F, Key.J, Key.K, Key.L, Key.Semicolon };
private SpecialColumnPosition specialColumnPosition; private SpecialColumnPosition specialColumnPosition;
/// <summary> /// <summary>
/// The style to use for the special column. /// The style to use for the special column.
@ -60,6 +53,8 @@ namespace osu.Game.Rulesets.Mania.UI
private List<Color4> normalColumnColours = new List<Color4>(); private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour; private Color4 specialColumnColour;
private readonly Container<DrawableManiaJudgement> judgements;
private readonly int columnCount; private readonly int columnCount;
public ManiaPlayfield(int columnCount) public ManiaPlayfield(int columnCount)
@ -72,21 +67,21 @@ namespace osu.Game.Rulesets.Mania.UI
Inverted.Value = true; Inverted.Value = true;
Container topLevelContainer;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new Container new Container
{ {
Name = "Playfield elements",
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Y,
Masking = true, AutoSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
new Container new Container
{ {
Name = "Masked elements", Name = "Columns mask",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X, AutoSizeAxes = Axes.X,
Masking = true, Masking = true,
@ -94,6 +89,7 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
new Box new Box
{ {
Name = "Background",
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4.Black Colour = Color4.Black
}, },
@ -105,27 +101,36 @@ namespace osu.Game.Rulesets.Mania.UI
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 }, Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0) Spacing = new Vector2(1, 0)
} },
} }
}, },
new Container new Container
{ {
Name = "Barlines mask",
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }, Width = 1366, // Bar lines should only be masked on the vertical axis
Children = new[] BypassAutoSizeAxes = Axes.Both,
Masking = true,
Child = content = new Container
{ {
content = new Container Name = "Bar lines",
{ Anchor = Anchor.TopCentre,
Name = "Bar lines", Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre, RelativeSizeAxes = Axes.Y,
Origin = Anchor.TopCentre, Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
RelativeSizeAxes = Axes.Y,
// Width is set in the Update method
}
} }
} },
judgements = new Container<DrawableManiaJudgement>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150,
BypassAutoSizeAxes = Axes.Both
},
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
} }
} }
}; };
@ -139,6 +144,8 @@ namespace osu.Game.Rulesets.Mania.UI
c.IsSpecial = isSpecialColumn(i); c.IsSpecial = isSpecialColumn(i);
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++; c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
columns.Add(c); columns.Add(c);
AddNested(c); AddNested(c);
} }
@ -150,6 +157,7 @@ namespace osu.Game.Rulesets.Mania.UI
private void invertedChanged(bool newValue) private void invertedChanged(bool newValue)
{ {
Scale = new Vector2(1, newValue ? -1 : 1); Scale = new Vector2(1, newValue ? -1 : 1);
judgements.Scale = Scale;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -184,6 +192,19 @@ namespace osu.Game.Rulesets.Mania.UI
} }
} }
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
var maniaObject = (ManiaHitObject)judgedObject.HitObject;
columns[maniaObject.Column].OnJudgement(judgedObject, judgement);
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
/// <summary> /// <summary>
/// Whether the column index is a special column for this playfield. /// Whether the column index is a special column for this playfield.
/// </summary> /// </summary>
@ -203,7 +224,8 @@ namespace osu.Game.Rulesets.Mania.UI
} }
} }
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h); public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h);
public void Add(DrawableBarLine barline) => HitObjects.Add(barline); public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
protected override void Update() protected override void Update()

View File

@ -15,26 +15,27 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject, ManiaJudgement> public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject>
{ {
/// <summary> /// <summary>
/// The number of columns which the <see cref="ManiaPlayfield"/> should display, and which /// The number of columns which the <see cref="ManiaPlayfield"/> should display, and which
/// the beatmap converter will attempt to convert beatmaps to use. /// the beatmap converter will attempt to convert beatmaps to use.
/// </summary> /// </summary>
private int availableColumns; public int AvailableColumns { get; private set; }
public IEnumerable<DrawableBarLine> BarLines; public IEnumerable<DrawableBarLine> BarLines;
@ -75,7 +76,7 @@ namespace osu.Game.Rulesets.Mania.UI
BarLines.ForEach(Playfield.Add); BarLines.ForEach(Playfield.Add);
} }
protected sealed override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(availableColumns) protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(AvailableColumns)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -83,29 +84,29 @@ namespace osu.Game.Rulesets.Mania.UI
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, availableColumns); public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, AvailableColumns);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
{ {
if (IsForCurrentRuleset) if (IsForCurrentRuleset)
availableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize)); AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize));
else else
{ {
float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count; float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
if (percentSliderOrSpinner < 0.2) if (percentSliderOrSpinner < 0.2)
availableColumns = 7; AvailableColumns = 7;
else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5) else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5)
availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6; AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6;
else if (percentSliderOrSpinner > 0.6) else if (percentSliderOrSpinner > 0.6)
availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4; AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4;
else else
availableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7)); AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7));
} }
return new ManiaBeatmapConverter(IsForCurrentRuleset, availableColumns); return new ManiaBeatmapConverter(IsForCurrentRuleset, AvailableColumns);
} }
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h) protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
{ {
ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action; ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;
@ -123,5 +124,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f); protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic); protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
} }
} }

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Mania</RootNamespace> <RootNamespace>osu.Game.Rulesets.Mania</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania</AssemblyName> <AssemblyName>osu.Game.Rulesets.Mania</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -60,7 +60,6 @@
<Compile Include="Judgements\HitWindows.cs" /> <Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\HoldNoteTailJudgement.cs" /> <Compile Include="Judgements\HoldNoteTailJudgement.cs" />
<Compile Include="Judgements\HoldNoteTickJudgement.cs" /> <Compile Include="Judgements\HoldNoteTickJudgement.cs" />
<Compile Include="Judgements\ManiaHitResult.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" /> <Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Mods\IGenerateSpeedAdjustments.cs" /> <Compile Include="Mods\IGenerateSpeedAdjustments.cs" />
@ -70,8 +69,12 @@
<Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" /> <Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" />
<Compile Include="Objects\Drawables\DrawableNote.cs" /> <Compile Include="Objects\Drawables\DrawableNote.cs" />
<Compile Include="Objects\Drawables\Pieces\BodyPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\BodyPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\GlowPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\LaneGlowPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
<Compile Include="Objects\Types\IHasColumn.cs" /> <Compile Include="Objects\Types\IHasColumn.cs" />
<Compile Include="Replays\ManiaAutoGenerator.cs" />
<Compile Include="Replays\ManiaFramedReplayInputHandler.cs" />
<Compile Include="Scoring\ManiaScoreProcessor.cs" /> <Compile Include="Scoring\ManiaScoreProcessor.cs" />
<Compile Include="Objects\BarLine.cs" /> <Compile Include="Objects\BarLine.cs" />
<Compile Include="Objects\HoldNote.cs" /> <Compile Include="Objects\HoldNote.cs" />
@ -83,6 +86,8 @@
<Compile Include="Timing\GravityScrollingContainer.cs" /> <Compile Include="Timing\GravityScrollingContainer.cs" />
<Compile Include="Timing\ScrollingAlgorithm.cs" /> <Compile Include="Timing\ScrollingAlgorithm.cs" />
<Compile Include="UI\Column.cs" /> <Compile Include="UI\Column.cs" />
<Compile Include="UI\DrawableManiaJudgement.cs" />
<Compile Include="UI\HitExplosion.cs" />
<Compile Include="UI\ManiaRulesetContainer.cs" /> <Compile Include="UI\ManiaRulesetContainer.cs" />
<Compile Include="UI\ManiaPlayfield.cs" /> <Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" /> <Compile Include="ManiaRuleset.cs" />
@ -96,14 +101,6 @@
<Project>{C76BF5B3-985E-4D39-95FE-97C9C879B83A}</Project> <Project>{C76BF5B3-985E-4D39-95FE-97C9C879B83A}</Project>
<Name>osu.Framework</Name> <Name>osu.Framework</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project> <Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project>
<Name>osu.Game</Name> <Name>osu.Game</Name>

View File

@ -4,10 +4,14 @@
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Framework.Extensions; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Judgements namespace osu.Game.Rulesets.Osu.Judgements
{ {
public class SliderTickJudgement : OsuJudgement
{
}
public class OsuJudgement : Judgement public class OsuJudgement : Judgement
{ {
/// <summary> /// <summary>
@ -15,38 +19,18 @@ namespace osu.Game.Rulesets.Osu.Judgements
/// </summary> /// </summary>
public Vector2 PositionOffset; public Vector2 PositionOffset;
/// <summary> protected override int NumericResultFor(HitResult result)
/// The score the user achieved.
/// </summary>
public OsuScoreResult Score;
/// <summary>
/// The score which would be achievable on a perfect hit.
/// </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);
private int scoreToInt(OsuScoreResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return 0; return 0;
case OsuScoreResult.Hit50: case HitResult.Meh:
return 50; return 50;
case OsuScoreResult.Hit100: case HitResult.Good:
return 100; return 100;
case OsuScoreResult.Hit300: case HitResult.Great:
return 300; return 300;
case OsuScoreResult.SliderTick:
return 10;
} }
} }

View File

@ -96,6 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModSpunOut : Mod public class OsuModSpunOut : Mod
{ {
public override string Name => "Spun Out"; public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout; public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed"; public override string Description => @"Spinners will be automatically completed";
public override double ScoreMultiplier => 0.9; public override double ScoreMultiplier => 0.9;
@ -106,6 +107,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModAutopilot : Mod public class OsuModAutopilot : Mod
{ {
public override string Name => "Autopilot"; public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot; public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm."; public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 0; public override double ScoreMultiplier => 0;
@ -126,6 +128,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModTarget : Mod public class OsuModTarget : Mod
{ {
public override string Name => "Target"; public override string Name => "Target";
public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target; public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @""; public override string Description => @"";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;

View File

@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
new Box new Box
{ {
Size = new Vector2(width), Size = new Vector2(width),
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Alpha = 0.5f, Alpha = 0.5f,

View File

@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -38,9 +39,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Colour = AccentColour, Colour = AccentColour,
Hit = () => Hit = () =>
{ {
if (Judgement.Result != HitResult.None) return false; if (AllJudged)
return false;
Judgement.PositionOffset = Vector2.Zero; //todo: set to correct value
UpdateJudgement(true); UpdateJudgement(true);
return true; return true;
}, },
@ -65,24 +66,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = circle.DrawSize; Size = circle.DrawSize;
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > HitObject.HitWindowFor(OsuScoreResult.Hit50)) if (timeOffset > HitObject.HitWindowFor(HitResult.Meh))
Judgement.Result = HitResult.Miss; AddJudgement(new OsuJudgement { Result = HitResult.Miss });
return; return;
} }
double hitOffset = Math.Abs(Judgement.TimeOffset); AddJudgement(new OsuJudgement
if (hitOffset < HitObject.HitWindowFor(OsuScoreResult.Hit50))
{ {
Judgement.Result = HitResult.Hit; Result = HitObject.ScoreResultForOffset(Math.Abs(timeOffset)),
Judgement.Score = HitObject.ScoreResultForOffset(hitOffset); PositionOffset = Vector2.Zero //todo: set to correct value
} });
else
Judgement.Result = HitResult.Miss;
} }
protected override void UpdateInitialState() protected override void UpdateInitialState()

View File

@ -3,12 +3,12 @@
using System.ComponentModel; using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using System.Linq;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject, OsuJudgement> public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
{ {
public const float TIME_PREEMPT = 600; public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400; public const float TIME_FADEIN = 400;
@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Alpha = 0; Alpha = 0;
} }
protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 };
protected sealed override void UpdateState(ArmedState state) protected sealed override void UpdateState(ArmedState state)
{ {
FinishTransforms(); FinishTransforms();
@ -33,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
UpdatePreemptState(); UpdatePreemptState();
using (BeginDelayedSequence(TIME_PREEMPT + Judgement.TimeOffset, true)) using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
UpdateCurrentState(state); UpdateCurrentState(state);
} }
} }
@ -65,18 +63,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
[Description(@"Amazing")] [Description(@"Amazing")]
Perfect Perfect
} }
public enum OsuScoreResult
{
[Description(@"Miss")]
Miss,
[Description(@"50")]
Hit50,
[Description(@"100")]
Hit100,
[Description(@"300")]
Hit300,
[Description(@"10")]
SliderTick
}
} }

View File

@ -9,9 +9,10 @@ using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableOsuJudgement : DrawableJudgement<OsuJudgement> public class DrawableOsuJudgement : DrawableJudgement
{ {
public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement) public DrawableOsuJudgement(OsuJudgement judgement)
: base(judgement)
{ {
} }

View File

@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -114,33 +115,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0); bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (initialCircle.Judgement?.Result != HitResult.Hit) if (!initialCircle.Judgements.Any(j => j.IsHit))
initialCircle.Position = slider.Curve.PositionAt(progress); initialCircle.Position = slider.Curve.PositionAt(progress);
foreach (var c in components) c.UpdateProgress(progress, repeat); foreach (var c in components) c.UpdateProgress(progress, repeat);
foreach (var t in ticks.Children) t.Tracking = ball.Tracking; foreach (var t in ticks.Children) t.Tracking = ball.Tracking;
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (!userTriggered && Time.Current >= slider.EndTime) if (!userTriggered && Time.Current >= slider.EndTime)
{ {
var ticksCount = ticks.Children.Count + 1; var ticksCount = ticks.Children.Count + 1;
var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit); var ticksHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit));
if (initialCircle.Judgement.Result == HitResult.Hit) if (initialCircle.Judgements.Any(j => j.IsHit))
ticksHit++; ticksHit++;
var hitFraction = (double)ticksHit / ticksCount; var hitFraction = (double)ticksHit / ticksCount;
if (hitFraction == 1 && initialCircle.Judgement.Score == OsuScoreResult.Hit300) if (hitFraction == 1 && initialCircle.Judgements.Any(j => j.Result == HitResult.Great))
Judgement.Score = OsuScoreResult.Hit300; AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (hitFraction >= 0.5 && initialCircle.Judgement.Score >= OsuScoreResult.Hit100) else if (hitFraction >= 0.5 && initialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
Judgement.Score = OsuScoreResult.Hit100; AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (hitFraction > 0) else if (hitFraction > 0)
Judgement.Score = OsuScoreResult.Hit50; AddJudgement(new OsuJudgement { Result = HitResult.Meh });
else else
Judgement.Score = OsuScoreResult.Miss; AddJudgement(new OsuJudgement { Result = HitResult.Miss });
Judgement.Result = Judgement.Score != OsuScoreResult.Miss ? HitResult.Hit : HitResult.Miss;
} }
} }

View File

@ -4,10 +4,10 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override bool RemoveWhenNotAlive => false; public override bool RemoveWhenNotAlive => false;
protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.SliderTick };
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{ {
this.sliderTick = sliderTick; this.sliderTick = sliderTick;
@ -49,13 +47,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}; };
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (Judgement.TimeOffset >= 0) if (timeOffset >= 0)
{ AddJudgement(new SliderTickJudgement { Result = Tracking ? HitResult.Perfect : HitResult.Miss });
Judgement.Result = Tracking ? HitResult.Hit : HitResult.Miss;
Judgement.Score = Tracking ? OsuScoreResult.SliderTick : OsuScoreResult.Miss;
}
} }
protected override void UpdatePreemptState() protected override void UpdatePreemptState()

View File

@ -11,6 +11,7 @@ using OpenTK.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -107,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (Time.Current < HitObject.StartTime) return; if (Time.Current < HitObject.StartTime) return;
@ -129,26 +130,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered && Time.Current >= spinner.EndTime) if (!userTriggered && Time.Current >= spinner.EndTime)
{ {
if (Progress >= 1) if (Progress >= 1)
{ AddJudgement(new OsuJudgement { Result = HitResult.Great });
Judgement.Score = OsuScoreResult.Hit300;
Judgement.Result = HitResult.Hit;
}
else if (Progress > .9) else if (Progress > .9)
{ AddJudgement(new OsuJudgement { Result = HitResult.Good });
Judgement.Score = OsuScoreResult.Hit100;
Judgement.Result = HitResult.Hit;
}
else if (Progress > .75) else if (Progress > .75)
{ AddJudgement(new OsuJudgement { Result = HitResult.Meh });
Judgement.Score = OsuScoreResult.Hit50; else if (Time.Current >= spinner.EndTime)
Judgement.Result = HitResult.Hit; AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
else
{
Judgement.Score = OsuScoreResult.Miss;
if (Time.Current >= spinner.EndTime)
Judgement.Result = HitResult.Miss;
}
} }
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
new TrianglesPiece new TrianglesPiece
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
Alpha = 0.5f, Alpha = 0.5f,
} }
}; };

View File

@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
BlendingMode = BlendingMode.Additive; Blending = BlendingMode.Additive;
Alpha = 0; Alpha = 0;
Children = new Drawable[] Children = new Drawable[]
{ {
new TrianglesPiece new TrianglesPiece
{ {
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Alpha = 0.2f, Alpha = 0.2f,
} }

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
BlendingMode = BlendingMode.Additive; Blending = BlendingMode.Additive;
Alpha = 0; Alpha = 0;
Children = new Drawable[] Children = new Drawable[]

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
Alpha = 0.5f Alpha = 0.5f
} }
}; };

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.slider = slider; this.slider = slider;
Masking = true; Masking = true;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
BlendingMode = BlendingMode.Additive; Blending = BlendingMode.Additive;
Origin = Anchor.Centre; Origin = Anchor.Centre;
BorderThickness = 10; BorderThickness = 10;
BorderColour = Color4.Orange; BorderColour = Color4.Orange;

View File

@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
path = new Path path = new Path
{ {
BlendingMode = BlendingMode.None, Blending = BlendingMode.None,
}, },
} }
}, },

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.isEnd = isEnd; this.isEnd = isEnd;
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
BlendingMode = BlendingMode.Additive; Blending = BlendingMode.Additive;
Origin = Anchor.Centre; Origin = Anchor.Centre;
Children = new Drawable[] Children = new Drawable[]

View File

@ -4,10 +4,10 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
{ {
@ -42,30 +42,30 @@ namespace osu.Game.Rulesets.Osu.Objects
public virtual bool NewCombo { get; set; } public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; } public int ComboIndex { get; set; }
public double HitWindowFor(OsuScoreResult result) public double HitWindowFor(HitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return 300; return 300;
case OsuScoreResult.Hit50: case HitResult.Meh:
return 150; return 150;
case OsuScoreResult.Hit100: case HitResult.Good:
return 80; return 80;
case OsuScoreResult.Hit300: case HitResult.Great:
return 30; return 30;
} }
} }
public OsuScoreResult ScoreResultForOffset(double offset) public HitResult ScoreResultForOffset(double offset)
{ {
if (offset < HitWindowFor(OsuScoreResult.Hit300)) if (offset < HitWindowFor(HitResult.Great))
return OsuScoreResult.Hit300; return HitResult.Great;
if (offset < HitWindowFor(OsuScoreResult.Hit100)) if (offset < HitWindowFor(HitResult.Good))
return OsuScoreResult.Hit100; return HitResult.Good;
if (offset < HitWindowFor(OsuScoreResult.Hit50)) if (offset < HitWindowFor(HitResult.Meh))
return OsuScoreResult.Hit50; return HitResult.Meh;
return OsuScoreResult.Miss; return HitResult.Miss;
} }
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)

View File

@ -51,6 +51,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
foreach (OsuDifficultyHitObject h in onScreen) foreach (OsuDifficultyHitObject h in onScreen)
{ {
// ReSharper disable once PossibleNullReferenceException (resharper not smart enough to understand IEnumerator.MoveNext())
h.TimeUntilHit -= latest.DeltaTime; h.TimeUntilHit -= latest.DeltaTime;
// Calculate reading strain here // Calculate reading strain here
} }

View File

@ -112,8 +112,6 @@ namespace osu.Game.Rulesets.Osu
} }
} }
public override Mod GetAutoplayMod() => new OsuModAutoplay();
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);

View File

@ -9,6 +9,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
@ -89,20 +90,20 @@ namespace osu.Game.Rulesets.Osu.Replays
double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime; double endTime = (prev as IHasEndTime)?.EndTime ?? prev.StartTime;
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot). // 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 (h.StartTime - h.HitWindowFor(HitResult.Miss) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) else if (h.StartTime - h.HitWindowFor(HitResult.Meh) > endTime + h.HitWindowFor(HitResult.Meh) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Meh), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Meh), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) else if (h.StartTime - h.HitWindowFor(HitResult.Good) > endTime + h.HitWindowFor(HitResult.Good) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(HitResult.Good), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(HitResult.Good), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
} }

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
@ -15,7 +16,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Scoring namespace osu.Game.Rulesets.Osu.Scoring
{ {
internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject, OsuJudgement> internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject>
{ {
public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>(ScoringMode.Exponential); public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>(ScoringMode.Exponential);
@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
{ {
} }
public OsuScoreProcessor(RulesetContainer<OsuHitObject, OsuJudgement> rulesetContainer) public OsuScoreProcessor(RulesetContainer<OsuHitObject> rulesetContainer)
: base(rulesetContainer) : base(rulesetContainer)
{ {
} }
@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
private int totalAccurateJudgements; private int totalAccurateJudgements;
private readonly Dictionary<OsuScoreResult, int> scoreResultCounts = new Dictionary<OsuScoreResult, int>(); private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>(); private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>();
private double comboMaxScore; private double comboMaxScore;
@ -42,18 +43,10 @@ namespace osu.Game.Rulesets.Osu.Scoring
hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate; hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate;
totalAccurateJudgements = beatmap.HitObjects.Count; totalAccurateJudgements = beatmap.HitObjects.Count;
foreach (var h in beatmap.HitObjects) foreach (var unused in beatmap.HitObjects)
{ {
if (h != null) // TODO: add support for other object types.
{ AddJudgement(new OsuJudgement { Result = HitResult.Great });
// TODO: add support for other object types.
AddJudgement(new OsuJudgement
{
MaxScore = OsuScoreResult.Hit300,
Score = OsuScoreResult.Hit300,
Result = HitResult.Hit
});
}
} }
} }
@ -72,44 +65,43 @@ namespace osu.Game.Rulesets.Osu.Scoring
{ {
base.PopulateScore(score); base.PopulateScore(score);
score.Statistics[@"300"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit300); score.Statistics[@"300"] = scoreResultCounts.GetOrDefault(HitResult.Great);
score.Statistics[@"100"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit100); score.Statistics[@"100"] = scoreResultCounts.GetOrDefault(HitResult.Good);
score.Statistics[@"50"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Hit50); score.Statistics[@"50"] = scoreResultCounts.GetOrDefault(HitResult.Meh);
score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(OsuScoreResult.Miss); score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(HitResult.Miss);
} }
protected override void OnNewJudgement(OsuJudgement judgement) protected override void OnNewJudgement(Judgement judgement)
{ {
if (judgement != null) var osuJudgement = (OsuJudgement)judgement;
if (judgement.Result != HitResult.None)
{ {
if (judgement.Result != HitResult.None) scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1;
{ comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1;
scoreResultCounts[judgement.Score] = scoreResultCounts.GetOrDefault(judgement.Score) + 1; }
comboResultCounts[judgement.Combo] = comboResultCounts.GetOrDefault(judgement.Combo) + 1;
}
switch (judgement.Score) switch (judgement.Result)
{ {
case OsuScoreResult.Hit300: case HitResult.Great:
Health.Value += (10.2 - hpDrainRate) * 0.02; Health.Value += (10.2 - hpDrainRate) * 0.02;
break; break;
case OsuScoreResult.Hit100: case HitResult.Good:
Health.Value += (8 - hpDrainRate) * 0.02; Health.Value += (8 - hpDrainRate) * 0.02;
break; break;
case OsuScoreResult.Hit50: case HitResult.Meh:
Health.Value += (4 - hpDrainRate) * 0.02; Health.Value += (4 - hpDrainRate) * 0.02;
break; break;
case OsuScoreResult.SliderTick: /*case HitResult.SliderTick:
Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01; Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01;
break; break;*/
case OsuScoreResult.Miss: case HitResult.Miss:
Health.Value -= hpDrainRate * 0.04; Health.Value -= hpDrainRate * 0.04;
break; break;
}
} }
calculateScore(); calculateScore();
@ -124,10 +116,10 @@ namespace osu.Game.Rulesets.Osu.Scoring
foreach (var j in Judgements) foreach (var j in Judgements)
{ {
baseScore += j.ScoreValue; baseScore += j.NumericResult;
baseMaxScore += j.MaxScoreValue; baseMaxScore += j.MaxNumericResult;
comboScore += j.ScoreValue * (1 + Combo.Value / 10d); comboScore += j.NumericResult * (1 + Combo.Value / 10d);
} }
Accuracy.Value = (double)baseScore / baseMaxScore; Accuracy.Value = (double)baseScore / baseMaxScore;

View File

@ -10,12 +10,13 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Osu.UI.Cursor;
namespace osu.Game.Rulesets.Osu.UI namespace osu.Game.Rulesets.Osu.UI
{ {
public class OsuPlayfield : Playfield<OsuHitObject, OsuJudgement> public class OsuPlayfield : Playfield
{ {
private readonly Container approachCircles; private readonly Container approachCircles;
private readonly Container judgementLayer; private readonly Container judgementLayer;
@ -67,11 +68,9 @@ namespace osu.Game.Rulesets.Osu.UI
AddInternal(new GameplayCursor()); AddInternal(new GameplayCursor());
} }
public override void Add(DrawableHitObject<OsuHitObject, OsuJudgement> h) public override void Add(DrawableHitObject h)
{ {
h.Depth = (float)h.HitObject.StartTime; var c = h as IDrawableHitObjectWithProxiedApproach;
IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null) if (c != null)
approachCircles.Add(c.ProxiedLayer.CreateProxy()); approachCircles.Add(c.ProxiedLayer.CreateProxy());
@ -85,12 +84,15 @@ namespace osu.Game.Rulesets.Osu.UI
.OrderBy(h => h.StartTime).OfType<OsuHitObject>(); .OrderBy(h => h.StartTime).OfType<OsuHitObject>();
} }
public override void OnJudgement(DrawableHitObject<OsuHitObject, OsuJudgement> judgedObject) public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{ {
DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgedObject.Judgement) var osuJudgement = (OsuJudgement)judgement;
var osuObject = (OsuHitObject)judgedObject.HitObject;
DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement)
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Position = judgedObject.HitObject.StackedEndPosition + judgedObject.Judgement.PositionOffset Position = osuObject.StackedEndPosition + osuJudgement.PositionOffset
}; };
judgementLayer.Add(explosion); judgementLayer.Add(explosion);

View File

@ -7,7 +7,6 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Replays;
@ -18,7 +17,7 @@ using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Osu.UI namespace osu.Game.Rulesets.Osu.UI
{ {
public class OsuRulesetContainer : RulesetContainer<OsuHitObject, OsuJudgement> public class OsuRulesetContainer : RulesetContainer<OsuHitObject>
{ {
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap, isForCurrentRuleset)
@ -31,11 +30,11 @@ namespace osu.Game.Rulesets.Osu.UI
protected override BeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor(); protected override BeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor();
protected override Playfield<OsuHitObject, OsuJudgement> CreatePlayfield() => new OsuPlayfield(); protected override Playfield CreatePlayfield() => new OsuPlayfield();
public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo);
protected override DrawableHitObject<OsuHitObject, OsuJudgement> GetVisualRepresentation(OsuHitObject h) protected override DrawableHitObject<OsuHitObject> GetVisualRepresentation(OsuHitObject h)
{ {
var circle = h as HitCircle; var circle = h as HitCircle;
if (circle != null) if (circle != null)

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Osu</RootNamespace> <RootNamespace>osu.Game.Rulesets.Osu</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu</AssemblyName> <AssemblyName>osu.Game.Rulesets.Osu</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>

View File

@ -1,34 +1,26 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Judgements namespace osu.Game.Rulesets.Taiko.Judgements
{ {
public class TaikoDrumRollTickJudgement : TaikoJudgement 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;
public override bool AffectsCombo => false; public override bool AffectsCombo => false;
protected override int NumericResultForScore(TaikoHitResult result) protected override int NumericResultFor(HitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return 0; return 0;
case TaikoHitResult.Great: case HitResult.Great:
return 200; return 200;
} }
} }
protected override int NumericResultForAccuracy(TaikoHitResult result) protected override int NumericResultForAccuracy(HitResult result)
{ {
return 0; return 0;
} }

View File

@ -1,15 +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.ComponentModel;
namespace osu.Game.Rulesets.Taiko.Judgements
{
public enum TaikoHitResult
{
[Description("GOOD")]
Good,
[Description("GREAT")]
Great
}
}

View File

@ -2,86 +2,56 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Framework.Extensions;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Judgements namespace osu.Game.Rulesets.Taiko.Judgements
{ {
public class TaikoJudgement : Judgement public class TaikoJudgement : Judgement
{ {
/// <summary>
/// The maximum result.
/// </summary>
public const TaikoHitResult MAX_HIT_RESULT = TaikoHitResult.Great;
/// <summary>
/// The result.
/// </summary>
public TaikoHitResult TaikoResult;
/// <summary>
/// The result value for the combo portion of the score.
/// </summary>
public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(TaikoResult);
/// <summary> /// <summary>
/// The result value for the accuracy portion of the score. /// The result value for the accuracy portion of the score.
/// </summary> /// </summary>
public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(TaikoResult); public int ResultNumericForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(Result);
/// <summary>
/// The maximum result value for the combo portion of the score.
/// </summary>
public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
/// <summary> /// <summary>
/// The maximum result value for the accuracy portion of the score. /// The maximum result value for the accuracy portion of the score.
/// </summary> /// </summary>
public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT); public int MaxResultValueForAccuracy => NumericResultForAccuracy(HitResult.Great);
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 strong hits.
/// </summary>
public virtual bool SecondHit { get; set; }
/// <summary> /// <summary>
/// Computes the numeric result value for the combo portion of the score. /// Computes the numeric result value for the combo portion of the score.
/// For the accuracy portion of the score (including accuracy percentage), see <see cref="NumericResultForAccuracy(TaikoHitResult)"/>. /// For the accuracy portion of the score (including accuracy percentage), see <see cref="NumericResultForAccuracy(HitResult)"/>.
/// </summary> /// </summary>
/// <param name="result">The result to compute the value for.</param> /// <param name="result">The result to compute the value for.</param>
/// <returns>The numeric result value.</returns> /// <returns>The numeric result value.</returns>
protected virtual int NumericResultForScore(TaikoHitResult result) protected override int NumericResultFor(HitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return 0; return 0;
case TaikoHitResult.Good: case HitResult.Good:
return 100; return 100;
case TaikoHitResult.Great: case HitResult.Great:
return 300; return 300;
} }
} }
/// <summary> /// <summary>
/// Computes the numeric result value for the accuracy portion of the score. /// Computes the numeric result value for the accuracy portion of the score.
/// For the combo portion of the score, see <see cref="NumericResultForScore(TaikoHitResult)"/>. /// For the combo portion of the score, see <see cref="NumericResultFor(HitResult)"/>.
/// </summary> /// </summary>
/// <param name="result">The result to compute the value for.</param> /// <param name="result">The result to compute the value for.</param>
/// <returns>The numeric result value.</returns> /// <returns>The numeric result value.</returns>
protected virtual int NumericResultForAccuracy(TaikoHitResult result) protected virtual int NumericResultForAccuracy(HitResult result)
{ {
switch (result) switch (result)
{ {
default: default:
return 0; return 0;
case TaikoHitResult.Good: case HitResult.Good:
return 150; return 150;
case TaikoHitResult.Great: case HitResult.Great:
return 300; return 300;
} }
} }

View File

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

View File

@ -5,14 +5,13 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
/// <summary> /// <summary>
/// A line that scrolls alongside hit objects in the playfield and visualises control points. /// A line that scrolls alongside hit objects in the playfield and visualises control points.
/// </summary> /// </summary>
public class DrawableBarLine : DrawableScrollingHitObject<TaikoHitObject, TaikoJudgement> public class DrawableBarLine : DrawableScrollingHitObject<TaikoHitObject>
{ {
/// <summary> /// <summary>
/// The width of the line tracker. /// The width of the line tracker.
@ -58,8 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}; };
} }
protected override TaikoJudgement CreateJudgement() => null;
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
} }

View File

@ -12,6 +12,7 @@ using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
@ -52,8 +53,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
} }
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong };
protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece();
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(TaikoAction action) => false;
@ -65,9 +64,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
accentDarkColour = colours.YellowDarker; accentDarkColour = colours.YellowDarker;
} }
private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj) private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
{ {
if (obj.Judgement.Result == HitResult.Hit) if (judgement.Result > HitResult.Miss)
rollingHits++; rollingHits++;
else else
rollingHits--; rollingHits--;
@ -78,23 +77,24 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.FadeAccent(newAccent, 100); MainPiece.FadeAccent(newAccent, 100);
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (userTriggered) if (userTriggered)
return; return;
if (Judgement.TimeOffset < 0) if (timeOffset < 0)
return; return;
int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit); int countHit = NestedHitObjects.Count(o => o.AllJudged);
if (countHit > HitObject.RequiredGoodHits) if (countHit > HitObject.RequiredGoodHits)
{ {
Judgement.Result = HitResult.Hit; AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good });
Judgement.TaikoResult = countHit >= HitObject.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good; if (HitObject.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
} }
else else
Judgement.Result = HitResult.Miss; AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
} }
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)

View File

@ -34,18 +34,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Filled = HitObject.FirstTick Filled = HitObject.FirstTick
}; };
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong }; protected override void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
return; return;
if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow) if (!(Math.Abs(timeOffset) < HitObject.HitWindow))
{ return;
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great; AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
} if (HitObject.IsStrong)
AddJudgement(new TaikoStrongHitJudgement());
} }
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
@ -58,9 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
} }
} }
public override bool OnPressed(TaikoAction action) public override bool OnPressed(TaikoAction action) => UpdateJudgement(true);
{
return Judgement.Result == HitResult.None && UpdateJudgement(true);
}
} }
} }

View File

@ -28,36 +28,30 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit; FillMode = FillMode.Fit;
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > HitObject.HitWindowGood) if (timeOffset > HitObject.HitWindowGood)
Judgement.Result = HitResult.Miss; AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
return; return;
} }
double hitOffset = Math.Abs(Judgement.TimeOffset); double hitOffset = Math.Abs(timeOffset);
if (hitOffset > HitObject.HitWindowMiss) if (hitOffset > HitObject.HitWindowMiss)
return; return;
if (!validKeyPressed) if (!validKeyPressed)
Judgement.Result = HitResult.Miss; AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
else if (hitOffset < HitObject.HitWindowGood) else if (hitOffset < HitObject.HitWindowGood)
{ AddJudgement(new TaikoJudgement { Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good });
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = hitOffset < HitObject.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
}
else else
Judgement.Result = HitResult.Miss; AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
} }
public override bool OnPressed(TaikoAction action) public override bool OnPressed(TaikoAction action)
{ {
if (Judgement.Result != HitResult.None)
return false;
validKeyPressed = HitActions.Contains(action); validKeyPressed = HitActions.Contains(action);
return UpdateJudgement(true); return UpdateJudgement(true);
@ -75,7 +69,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
var circlePiece = MainPiece as CirclePiece; var circlePiece = MainPiece as CirclePiece;
circlePiece?.FlashBox.FinishTransforms(); circlePiece?.FlashBox.FinishTransforms();
using (BeginDelayedSequence(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true)) var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime;
using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true))
{ {
switch (State) switch (State)
{ {

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@ -25,13 +24,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
} }
protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement(); private bool processedSecondHit;
public override bool AllJudged => processedSecondHit && base.AllJudged;
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (Judgement.Result == HitResult.None) if (!base.AllJudged)
{ {
base.CheckJudgement(userTriggered); base.CheckForJudgements(userTriggered, timeOffset);
return; return;
} }
@ -41,7 +41,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
// If we get here, we're assured that the key pressed is the correct secondary key // 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) if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
Judgement.SecondHit = true; {
AddJudgement(new TaikoStrongHitJudgement());
processedSecondHit = true;
}
} }
public override bool OnReleased(TaikoAction action) public override bool OnReleased(TaikoAction action)
@ -54,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public override bool OnPressed(TaikoAction action) public override bool OnPressed(TaikoAction action)
{ {
// Check if we've handled the first key // Check if we've handled the first key
if (Judgement.Result == HitResult.None) if (!base.AllJudged)
{ {
// First key hasn't been handled yet, attempt to handle it // First key hasn't been handled yet, attempt to handle it
bool handled = base.OnPressed(action); bool handled = base.OnPressed(action);
@ -70,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
} }
// If we've already hit the second key, don't handle this object any further // If we've already hit the second key, don't handle this object any further
if (Judgement.SecondHit) if (processedSecondHit)
return false; return false;
// Don't handle represses of the first key // Don't handle represses of the first key

View File

@ -9,11 +9,11 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Origin = Anchor.Centre, Origin = Anchor.Centre,
Alpha = 0, Alpha = 0,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
Masking = true, Masking = true,
Children = new[] Children = new[]
{ {
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true, Masking = true,
BorderThickness = target_ring_thick_border, BorderThickness = target_ring_thick_border,
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Width *= Parent.RelativeChildSize.X; Width *= Parent.RelativeChildSize.X;
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (userTriggered) if (userTriggered)
{ {
@ -153,24 +153,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
if (userHits == HitObject.RequiredHits) if (userHits == HitObject.RequiredHits)
{ AddJudgement(new TaikoJudgement { Result = HitResult.Great });
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great;
}
} }
else else
{ {
if (Judgement.TimeOffset < 0) if (timeOffset < 0)
return; return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
if (userHits > HitObject.RequiredHits / 2) AddJudgement(userHits > HitObject.RequiredHits / 2
{ ? new TaikoJudgement { Result = HitResult.Good }
Judgement.Result = HitResult.Hit; : new TaikoJudgement { Result = HitResult.Miss });
Judgement.TaikoResult = TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
} }
} }
@ -180,7 +173,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
const float out_transition_time = 300; const float out_transition_time = 300;
double untilStartTime = HitObject.StartTime - Time.Current; double untilStartTime = HitObject.StartTime - Time.Current;
double untilJudgement = untilStartTime + Judgement.TimeOffset + HitObject.Duration; double untilJudgement = untilStartTime + (Judgements.FirstOrDefault()?.TimeOffset ?? 0) + HitObject.Duration;
targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint); targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint);
this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out); this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out);
@ -214,9 +207,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public override bool OnPressed(TaikoAction action) public override bool OnPressed(TaikoAction action)
{ {
if (Judgement.Result != HitResult.None)
return false;
// Don't handle keys before the swell starts // Don't handle keys before the swell starts
if (Time.Current < HitObject.StartTime) if (Time.Current < HitObject.StartTime)
return false; return false;

View File

@ -4,15 +4,13 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
public abstract class DrawableTaikoHitObject<TaikoHitType> public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableScrollingHitObject<TaikoHitObject>, IKeyBindingHandler<TaikoAction>
: DrawableScrollingHitObject<TaikoHitObject, TaikoJudgement>, IKeyBindingHandler<TaikoAction> where TaikoHitType : TaikoHitObject
where TaikoHitType : TaikoHitObject
{ {
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
@ -37,8 +35,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.KiaiMode = HitObject.Kiai; MainPiece.KiaiMode = HitObject.Kiai;
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
public abstract bool OnPressed(TaikoAction action); public abstract bool OnPressed(TaikoAction action);

View File

@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4.White, Colour = Color4.White,
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
Alpha = 0, Alpha = 0,
AlwaysPresent = true AlwaysPresent = true
} }

View File

@ -1,8 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
@ -29,19 +27,5 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// Strong hit objects give more points for hitting the hit object with both keys. /// Strong hit objects give more points for hitting the hit object with both keys.
/// </summary> /// </summary>
public bool IsStrong; public bool IsStrong;
/// <summary>
/// Whether this HitObject is in Kiai time.
/// </summary>
public bool Kiai { get; protected set; }
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(controlPointInfo, difficulty);
EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
Kiai |= effectPoint.KiaiMode;
}
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
@ -12,7 +13,7 @@ using OpenTK;
namespace osu.Game.Rulesets.Taiko.Scoring namespace osu.Game.Rulesets.Taiko.Scoring
{ {
internal class TaikoScoreProcessor : ScoreProcessor<TaikoHitObject, TaikoJudgement> internal class TaikoScoreProcessor : ScoreProcessor<TaikoHitObject>
{ {
/// <summary> /// <summary>
/// The maximum score achievable. /// The maximum score achievable.
@ -36,12 +37,12 @@ namespace osu.Game.Rulesets.Taiko.Scoring
private const double combo_base = 4; private const double combo_base = 4;
/// <summary> /// <summary>
/// The HP awarded by a <see cref="TaikoHitResult.Great"/> hit. /// The HP awarded by a <see cref="HitResult.Great"/> hit.
/// </summary> /// </summary>
private const double hp_hit_great = 0.03; private const double hp_hit_great = 0.03;
/// <summary> /// <summary>
/// The HP awarded for a <see cref="TaikoHitResult.Good"/> hit. /// The HP awarded for a <see cref="HitResult.Good"/> hit.
/// </summary> /// </summary>
private const double hp_hit_good = 0.011; private const double hp_hit_good = 0.011;
@ -113,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
{ {
} }
public TaikoScoreProcessor(RulesetContainer<TaikoHitObject, TaikoJudgement> rulesetContainer) public TaikoScoreProcessor(RulesetContainer<TaikoHitObject> rulesetContainer)
: base(rulesetContainer) : base(rulesetContainer)
{ {
} }
@ -138,39 +139,28 @@ namespace osu.Game.Rulesets.Taiko.Scoring
{ {
if (obj is Hit) if (obj is Hit)
{ {
AddJudgement(new TaikoJudgement AddJudgement(new TaikoJudgement { Result = HitResult.Great });
{ if (obj.IsStrong)
Result = HitResult.Hit, AddJudgement(new TaikoStrongHitJudgement());
TaikoResult = TaikoHitResult.Great,
SecondHit = obj.IsStrong
});
} }
else if (obj is DrumRoll) else if (obj is DrumRoll)
{ {
for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++) for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++)
{ {
AddJudgement(new TaikoDrumRollTickJudgement AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
{
Result = HitResult.Hit, if (obj.IsStrong)
TaikoResult = TaikoHitResult.Great, AddJudgement(new TaikoStrongHitJudgement());
SecondHit = obj.IsStrong
});
} }
AddJudgement(new TaikoJudgement AddJudgement(new TaikoJudgement { Result = HitResult.Great });
{
Result = HitResult.Hit, if (obj.IsStrong)
TaikoResult = TaikoHitResult.Great, AddJudgement(new TaikoStrongHitJudgement());
SecondHit = obj.IsStrong
});
} }
else if (obj is Swell) else if (obj is Swell)
{ {
AddJudgement(new TaikoJudgement AddJudgement(new TaikoJudgement { Result = HitResult.Great });
{
Result = HitResult.Hit,
TaikoResult = TaikoHitResult.Great
});
} }
} }
@ -178,16 +168,40 @@ namespace osu.Game.Rulesets.Taiko.Scoring
maxComboPortion = comboPortion; maxComboPortion = comboPortion;
} }
protected override void OnNewJudgement(TaikoJudgement judgement) protected override void OnNewJudgement(Judgement judgement)
{ {
bool isStrong = judgement is TaikoStrongHitJudgement;
bool isTick = judgement is TaikoDrumRollTickJudgement; bool isTick = judgement is TaikoDrumRollTickJudgement;
// Don't consider ticks as a type of hit that counts towards map completion // Don't consider ticks and strong hits as a type of hit that counts towards map completion
if (!isTick) if (!isTick && !isStrong)
totalHits++; totalHits++;
// Apply score changes // Apply score changes
addHitScore(judgement); if (judgement.IsHit)
{
double baseValue = judgement.NumericResult;
if (isStrong)
{
// Add increased score for the previous judgement by hitting a strong hit object with the second key
var prevJudgement = Judgements[Judgements.Count - 1];
baseValue = prevJudgement.NumericResult * strongHitScale;
}
// Add score to portions
if (judgement is TaikoDrumRollTickJudgement)
bonusScore += baseValue;
else
{
// A relevance factor that needs to be applied to make higher combos more relevant
// Value is capped at 400 combo
double comboRelevance = Math.Min(Math.Log(400, combo_base), Math.Max(0.5, Math.Log(Combo.Value, combo_base)));
comboPortion += baseValue * comboRelevance;
}
}
// Apply HP changes // Apply HP changes
switch (judgement.Result) switch (judgement.Result)
@ -197,66 +211,26 @@ namespace osu.Game.Rulesets.Taiko.Scoring
if (!isTick) if (!isTick)
Health.Value += hpIncreaseMiss; Health.Value += hpIncreaseMiss;
break; break;
case HitResult.Hit: case HitResult.Good:
switch (judgement.TaikoResult) Health.Value += hpIncreaseGood;
{ break;
case TaikoHitResult.Good: case HitResult.Great:
Health.Value += hpIncreaseGood; if (isTick)
break; Health.Value += hpIncreaseTick;
case TaikoHitResult.Great: else
if (isTick) Health.Value += hpIncreaseGreat;
Health.Value += hpIncreaseTick;
else
Health.Value += hpIncreaseGreat;
break;
}
break; break;
} }
calculateScore();
}
protected override void OnJudgementChanged(TaikoJudgement judgement)
{
// Apply score changes
addHitScore(judgement);
calculateScore();
}
private void addHitScore(TaikoJudgement judgement)
{
if (judgement.Result != HitResult.Hit)
return;
double baseValue = judgement.ResultValueForScore;
// Add increased score for hitting a strong hit object with the second key
if (judgement.SecondHit)
baseValue *= strongHitScale;
// Add score to portions
if (judgement is TaikoDrumRollTickJudgement)
bonusScore += baseValue;
else
{
// A relevance factor that needs to be applied to make higher combos more relevant
// Value is capped at 400 combo
double comboRelevance = Math.Min(Math.Log(400, combo_base), Math.Max(0.5, Math.Log(Combo.Value, combo_base)));
comboPortion += baseValue * comboRelevance;
}
}
private void calculateScore()
{
int scoreForAccuracy = 0; int scoreForAccuracy = 0;
int maxScoreForAccuracy = 0; int maxScoreForAccuracy = 0;
foreach (var j in Judgements) foreach (var j in Judgements)
{ {
scoreForAccuracy += j.ResultValueForAccuracy; var taikoJudgement = (TaikoJudgement)j;
maxScoreForAccuracy += j.MaxResultValueForAccuracy;
scoreForAccuracy += taikoJudgement.ResultNumericForAccuracy;
maxScoreForAccuracy += taikoJudgement.MaxResultValueForAccuracy;
} }
Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy; Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;

View File

@ -95,8 +95,6 @@ namespace osu.Game.Rulesets.Taiko
} }
} }
public override Mod GetAutoplayMod() => new TaikoModAutoplay();
public override string Description => "osu!taiko"; public override string Description => "osu!taiko";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -13,15 +12,19 @@ namespace osu.Game.Rulesets.Taiko.UI
/// <summary> /// <summary>
/// Text that is shown as judgement when a hit object is hit or missed. /// Text that is shown as judgement when a hit object is hit or missed.
/// </summary> /// </summary>
public class DrawableTaikoJudgement : DrawableJudgement<TaikoJudgement> public class DrawableTaikoJudgement : DrawableJudgement
{ {
public readonly DrawableHitObject JudgedObject;
/// <summary> /// <summary>
/// Creates a new judgement text. /// Creates a new judgement text.
/// </summary> /// </summary>
/// <param name="judgedObject">The object which is being judged.</param>
/// <param name="judgement">The judgement to visualise.</param> /// <param name="judgement">The judgement to visualise.</param>
public DrawableTaikoJudgement(TaikoJudgement judgement) public DrawableTaikoJudgement(DrawableHitObject judgedObject, Judgement judgement)
: base(judgement) : base(judgement)
{ {
JudgedObject = judgedObject;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -29,28 +32,19 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
switch (Judgement.Result) switch (Judgement.Result)
{ {
case HitResult.Hit: case HitResult.Good:
switch (Judgement.TaikoResult) Colour = colours.GreenLight;
{ break;
case TaikoHitResult.Good: case HitResult.Great:
Colour = colours.GreenLight; Colour = colours.BlueLight;
break;
case TaikoHitResult.Great:
Colour = colours.BlueLight;
break;
}
break; break;
} }
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
switch (Judgement.Result) if (Judgement.IsHit)
{ this.MoveToY(-100, 500);
case HitResult.Hit:
this.MoveToY(-100, 500);
break;
}
base.LoadComplete(); base.LoadComplete();
} }

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
@ -18,17 +18,17 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary> /// </summary>
internal class HitExplosion : CircularContainer internal class HitExplosion : CircularContainer
{ {
public readonly TaikoJudgement Judgement; public readonly DrawableHitObject JudgedObject;
private readonly Box innerFill; private readonly Box innerFill;
private readonly bool isRim; private readonly bool isRim;
public HitExplosion(TaikoJudgement judgement, bool isRim) public HitExplosion(DrawableHitObject judgedObject, bool isRim)
{ {
this.isRim = isRim; this.isRim = isRim;
Judgement = judgement; JudgedObject = judgedObject;
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre; Origin = Anchor.Centre;

View File

@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Alpha = 0, Alpha = 0,
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
}, },
centre = new Sprite centre = new Sprite
{ {
@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f), Size = new Vector2(0.7f),
Alpha = 0, Alpha = 0,
BlendingMode = BlendingMode.Additive Blending = BlendingMode.Additive
} }
}; };
} }

View File

@ -7,22 +7,22 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
{ {
public class KiaiHitExplosion : CircularContainer public class KiaiHitExplosion : CircularContainer
{ {
public readonly TaikoJudgement Judgement; public readonly DrawableHitObject JudgedObject;
private readonly bool isRim; private readonly bool isRim;
public KiaiHitExplosion(TaikoJudgement judgement, bool isRim) public KiaiHitExplosion(DrawableHitObject judgedObject, bool isRim)
{ {
this.isRim = isRim; this.isRim = isRim;
Judgement = judgement; JudgedObject = judgedObject;
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre; Origin = Anchor.Centre;

View File

@ -14,11 +14,12 @@ using osu.Game.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
{ {
public class TaikoPlayfield : ScrollingPlayfield<TaikoHitObject, TaikoJudgement> public class TaikoPlayfield : ScrollingPlayfield
{ {
/// <summary> /// <summary>
/// Default height of a <see cref="TaikoPlayfield"/> when inside a <see cref="TaikoRulesetContainer"/>. /// Default height of a <see cref="TaikoPlayfield"/> when inside a <see cref="TaikoRulesetContainer"/>.
@ -97,7 +98,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit, FillMode = FillMode.Fit,
BlendingMode = BlendingMode.Additive, Blending = BlendingMode.Additive,
}, },
new HitTarget new HitTarget
{ {
@ -126,14 +127,14 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit, FillMode = FillMode.Fit,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
BlendingMode = BlendingMode.Additive Blending = BlendingMode.Additive
}, },
judgementContainer = new Container<DrawableTaikoJudgement> judgementContainer = new Container<DrawableTaikoJudgement>
{ {
Name = "Judgements", Name = "Judgements",
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
BlendingMode = BlendingMode.Additive Blending = BlendingMode.Additive
}, },
} }
}, },
@ -202,10 +203,8 @@ namespace osu.Game.Rulesets.Taiko.UI
background.Colour = colours.Gray0; background.Colour = colours.Gray0;
} }
public override void Add(DrawableHitObject<TaikoHitObject, TaikoJudgement> h) public override void Add(DrawableHitObject h)
{ {
h.Depth = (float)h.HitObject.StartTime;
base.Add(h); base.Add(h);
var barline = h as DrawableBarLine; var barline = h as DrawableBarLine;
@ -218,25 +217,27 @@ namespace osu.Game.Rulesets.Taiko.UI
swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy());
} }
public override void OnJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> judgedObject) public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{ {
bool wasHit = judgedObject.Judgement.Result == HitResult.Hit; if (judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
bool secondHit = judgedObject.Judgement.SecondHit;
judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement)
{ {
Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft, judgementContainer.Add(new DrawableTaikoJudgement(judgedObject, judgement)
Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre, {
RelativePositionAxes = Axes.X, Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
X = wasHit ? judgedObject.Position.X : 0, Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre,
}); RelativePositionAxes = Axes.X,
X = judgement.IsHit ? judgedObject.Position.X : 0,
});
}
if (!wasHit) if (!judgement.IsHit)
return; return;
bool isRim = judgedObject.HitObject is RimHit; bool isRim = judgedObject.HitObject is RimHit;
if (!secondHit) if (judgement is TaikoStrongHitJudgement)
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit();
else
{ {
if (judgedObject.X >= -0.05f && judgedObject is DrawableHit) if (judgedObject.X >= -0.05f && judgedObject is DrawableHit)
{ {
@ -244,13 +245,11 @@ namespace osu.Game.Rulesets.Taiko.UI
topLevelHitContainer.Add(judgedObject.CreateProxy()); topLevelHitContainer.Add(judgedObject.CreateProxy());
} }
hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement, isRim)); hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
if (judgedObject.HitObject.Kiai) if (judgedObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim)); kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
} }
else
hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
} }
} }
} }

View File

@ -9,7 +9,6 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.Taiko.Scoring;
@ -22,7 +21,7 @@ using osu.Framework.Input;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
{ {
public class TaikoRulesetContainer : ScrollingRulesetContainer<TaikoPlayfield, TaikoHitObject, TaikoJudgement> public class TaikoRulesetContainer : ScrollingRulesetContainer<TaikoPlayfield, TaikoHitObject>
{ {
public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap, isForCurrentRuleset)
@ -95,13 +94,13 @@ namespace osu.Game.Rulesets.Taiko.UI
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
protected override Playfield<TaikoHitObject, TaikoJudgement> CreatePlayfield() => new TaikoPlayfield protected override Playfield CreatePlayfield() => new TaikoPlayfield
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft Origin = Anchor.CentreLeft
}; };
protected override DrawableHitObject<TaikoHitObject, TaikoJudgement> GetVisualRepresentation(TaikoHitObject h) protected override DrawableHitObject<TaikoHitObject> GetVisualRepresentation(TaikoHitObject h)
{ {
var centreHit = h as CentreHit; var centreHit = h as CentreHit;
if (centreHit != null) if (centreHit != null)

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Taiko</RootNamespace> <RootNamespace>osu.Game.Rulesets.Taiko</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Taiko</AssemblyName> <AssemblyName>osu.Game.Rulesets.Taiko</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -52,7 +52,6 @@
<Compile Include="Judgements\TaikoDrumRollTickJudgement.cs" /> <Compile Include="Judgements\TaikoDrumRollTickJudgement.cs" />
<Compile Include="Judgements\TaikoStrongHitJudgement.cs" /> <Compile Include="Judgements\TaikoStrongHitJudgement.cs" />
<Compile Include="Judgements\TaikoJudgement.cs" /> <Compile Include="Judgements\TaikoJudgement.cs" />
<Compile Include="Judgements\TaikoHitResult.cs" />
<Compile Include="Objects\BarLine.cs" /> <Compile Include="Objects\BarLine.cs" />
<Compile Include="Objects\Drawables\DrawableBarLine.cs" /> <Compile Include="Objects\Drawables\DrawableBarLine.cs" />
<Compile Include="Objects\Drawables\DrawableBarLineMajor.cs" /> <Compile Include="Objects\Drawables\DrawableBarLineMajor.cs" />

View File

@ -7,7 +7,7 @@
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace>osu.Game.Tests</RootNamespace> <RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests</AssemblyName> <AssemblyName>osu.Game.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>

View File

@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps
public class Beatmap<T> public class Beatmap<T>
where T : HitObject where T : HitObject
{ {
public BeatmapInfo BeatmapInfo; public BeatmapInfo BeatmapInfo = new BeatmapInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo(); public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>(); public List<BreakPeriod> Breaks = new List<BreakPeriod>();
public readonly List<Color4> ComboColors = new List<Color4> public readonly List<Color4> ComboColors = new List<Color4>
@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// The HitObjects this Beatmap contains. /// The HitObjects this Beatmap contains.
/// </summary> /// </summary>
public List<T> HitObjects; public List<T> HitObjects = new List<T>();
/// <summary> /// <summary>
/// Total amount of break time in the beatmap. /// Total amount of break time in the beatmap.
@ -44,12 +44,13 @@ namespace osu.Game.Beatmaps
/// Constructs a new beatmap. /// Constructs a new beatmap.
/// </summary> /// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param> /// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null) public Beatmap(Beatmap<T> original = null)
{ {
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo; BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo; ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks; Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors; ComboColors = original?.ComboColors ?? ComboColors;
HitObjects = original?.HitObjects ?? HitObjects;
} }
} }
@ -65,7 +66,6 @@ namespace osu.Game.Beatmaps
public Beatmap(Beatmap original = null) public Beatmap(Beatmap original = null)
: base(original) : base(original)
{ {
HitObjects = original?.HitObjects;
} }
} }
} }

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