1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-05 19:52:58 +08:00

Merge branch 'master' into taiko_hit_drawables

This commit is contained in:
Dan Balasescu 2017-03-24 14:55:07 +09:00 committed by GitHub
commit bf3846eedb
214 changed files with 4036 additions and 1302 deletions

@ -1 +1 @@
Subproject commit e6394035d443d4498b71e845e5763dd3faf98c7c Subproject commit 51737ec1320b4ea9dce4978f24e1d77a28f0a98e

View File

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

View File

@ -0,0 +1,24 @@
// 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.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
namespace osu.Desktop.VisualTests.Beatmaps
{
public class TestWorkingBeatmap : WorkingBeatmap
{
public TestWorkingBeatmap(Beatmap beatmap)
: base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet)
{
this.beatmap = beatmap;
}
private readonly Beatmap beatmap;
protected override Beatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
protected override Track GetTrack() => null;
}
}

View File

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

View File

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

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Game.Screens.Tournament; using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams; using osu.Game.Screens.Tournament.Teams;
using osu.Game.Users;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
@ -24,57 +25,57 @@ namespace osu.Desktop.VisualTests.Tests
private class TestTeamList : ITeamList private class TestTeamList : ITeamList
{ {
public IEnumerable<Team> Teams { get; } = new[] public IEnumerable<Country> Teams { get; } = new[]
{ {
new Team new Country
{ {
FlagName = "GB", FlagName = "GB",
FullName = "United Kingdom", FullName = "United Kingdom",
Acronym = "UK" Acronym = "UK"
}, },
new Team new Country
{ {
FlagName = "FR", FlagName = "FR",
FullName = "France", FullName = "France",
Acronym = "FRA" Acronym = "FRA"
}, },
new Team new Country
{ {
FlagName = "CN", FlagName = "CN",
FullName = "China", FullName = "China",
Acronym = "CHN" Acronym = "CHN"
}, },
new Team new Country
{ {
FlagName = "AU", FlagName = "AU",
FullName = "Australia", FullName = "Australia",
Acronym = "AUS" Acronym = "AUS"
}, },
new Team new Country
{ {
FlagName = "JP", FlagName = "JP",
FullName = "Japan", FullName = "Japan",
Acronym = "JPN" Acronym = "JPN"
}, },
new Team new Country
{ {
FlagName = "RO", FlagName = "RO",
FullName = "Romania", FullName = "Romania",
Acronym = "ROM" Acronym = "ROM"
}, },
new Team new Country
{ {
FlagName = "IT", FlagName = "IT",
FullName = "Italy", FullName = "Italy",
Acronym = "PIZZA" Acronym = "PIZZA"
}, },
new Team new Country
{ {
FlagName = "VE", FlagName = "VE",
FullName = "Venezuela", FullName = "Venezuela",
Acronym = "VNZ" Acronym = "VNZ"
}, },
new Team new Country
{ {
FlagName = "US", FlagName = "US",
FullName = "United States of America", FullName = "United States of America",

View File

@ -8,7 +8,6 @@ using osu.Framework.MathUtils;
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.IO;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes.Catch.UI; using osu.Game.Modes.Catch.UI;
using osu.Game.Modes.Mania.UI; using osu.Game.Modes.Mania.UI;
@ -17,6 +16,7 @@ using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.UI; using osu.Game.Modes.Osu.UI;
using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.Taiko.UI;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Desktop.VisualTests.Beatmaps;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
@ -95,16 +95,5 @@ namespace osu.Desktop.VisualTests.Tests
} }
}); });
} }
private class TestWorkingBeatmap : WorkingBeatmap
{
public TestWorkingBeatmap(Beatmap beatmap)
: base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet)
{
Beatmap = beatmap;
}
protected override ArchiveReader GetReader() => null;
}
} }
} }

View File

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

View File

@ -0,0 +1,225 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Screens.Testing;
using osu.Game.Modes.Mods;
using osu.Game.Modes.Osu.Mods;
using osu.Game.Modes.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseLeaderboard : TestCase
{
public override string Description => @"From song select";
private Leaderboard leaderboard;
private void newScores()
{
var scores = new[]
{
new Score
{
Rank = ScoreRank.XH,
Accuracy = 100,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 6602580,
Username = @"waaiiru",
Country = new Country
{
FullName = @"Spain",
FlagName = @"ES",
},
},
},
new Score
{
Rank = ScoreRank.X,
Accuracy = 100,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 4608074,
Username = @"Skycries",
Country = new Country
{
FullName = @"Brazil",
FlagName = @"BR",
},
},
},
new Score
{
Rank = ScoreRank.SH,
Accuracy = 100,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 1014222,
Username = @"eLy",
Country = new Country
{
FullName = @"Japan",
FlagName = @"JP",
},
},
},
new Score
{
Rank = ScoreRank.S,
Accuracy = 100,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 1541390,
Username = @"Toukai",
Country = new Country
{
FullName = @"Canada",
FlagName = @"CA",
},
},
},
new Score
{
Rank = ScoreRank.A,
Accuracy = 100,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 2243452,
Username = @"Satoruu",
Country = new Country
{
FullName = @"Venezuela",
FlagName = @"VE",
},
},
},
new Score
{
Rank = ScoreRank.B,
Accuracy = 98.26,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 2705430,
Username = @"Mooha",
Country = new Country
{
FullName = @"France",
FlagName = @"FR",
},
},
},
new Score
{
Rank = ScoreRank.C,
Accuracy = 96.54,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 7151382,
Username = @"Mayuri Hana",
Country = new Country
{
FullName = @"Thailand",
FlagName = @"TH",
},
},
},
new Score
{
Rank = ScoreRank.F,
Accuracy = 60.25,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 2051389,
Username = @"FunOrange",
Country = new Country
{
FullName = @"Canada",
FlagName = @"CA",
},
},
},
new Score
{
Rank = ScoreRank.F,
Accuracy = 51.40,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 6169483,
Username = @"-Hebel-",
Country = new Country
{
FullName = @"Mexico",
FlagName = @"MX",
},
},
},
new Score
{
Rank = ScoreRank.F,
Accuracy = 42.22,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
User = new User
{
Id = 6702666,
Username = @"prhtnsm",
Country = new Country
{
FullName = @"Germany",
FlagName = @"DE",
},
},
},
};
leaderboard.Scores = scores;
}
public override void Reset()
{
base.Reset();
Add(leaderboard = new Leaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
});
AddButton(@"New Scores", newScores);
newScores();
}
}
}

View File

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

View File

@ -8,13 +8,13 @@ using osu.Framework.Screens.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using OpenTK; using OpenTK;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.IO;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Desktop.VisualTests.Beatmaps;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
@ -23,7 +23,6 @@ namespace osu.Desktop.VisualTests.Tests
protected Player Player; protected Player Player;
private BeatmapDatabase db; private BeatmapDatabase db;
public override string Description => @"Showing everything to play the game."; public override string Description => @"Showing everything to play the game.";
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -97,16 +96,5 @@ namespace osu.Desktop.VisualTests.Tests
Beatmap = beatmap Beatmap = beatmap
}; };
} }
private class TestWorkingBeatmap : WorkingBeatmap
{
public TestWorkingBeatmap(Beatmap beatmap)
: base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet)
{
Beatmap = beatmap;
}
protected override ArchiveReader GetReader() => null;
}
} }
} }

View File

@ -0,0 +1,77 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.MathUtils;
using osu.Framework.Screens.Testing;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.Taiko.UI;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseTaikoPlayfield : TestCase
{
public override string Description => "Taiko playfield";
private TaikoPlayfield playfield;
public override void Reset()
{
base.Reset();
AddButton("Hit!", addHitJudgement);
AddButton("Miss :(", addMissJudgement);
Add(playfield = new TaikoPlayfield
{
Y = 200
});
}
private void addHitJudgement()
{
TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great;
playfield.OnJudgement(new DrawableTestHit(new Hit())
{
X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f),
Judgement = new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = hitResult,
TimeOffset = 0,
ComboAtHit = 1,
SecondHit = RNG.Next(10) == 0
}
});
}
private void addMissJudgement()
{
playfield.OnJudgement(new DrawableTestHit(new Hit())
{
Judgement = new TaikoJudgement
{
Result = HitResult.Miss,
TimeOffset = 0,
ComboAtHit = 0
}
});
}
private class DrawableTestHit : DrawableHitObject<TaikoHitObject, TaikoJudgement>
{
public DrawableTestHit(TaikoHitObject hitObject)
: base(hitObject)
{
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{
}
}
}
}

View File

@ -23,7 +23,7 @@
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone> <TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<Install>true</Install> <Install>true</Install>
<InstallFrom>Disk</InstallFrom> <InstallFrom>Disk</InstallFrom>
@ -194,6 +194,7 @@
<Compile Include="Tests\TestCaseReplay.cs" /> <Compile Include="Tests\TestCaseReplay.cs" />
<Compile Include="Tests\TestCaseScoreCounter.cs" /> <Compile Include="Tests\TestCaseScoreCounter.cs" />
<Compile Include="Tests\TestCaseTabControl.cs" /> <Compile Include="Tests\TestCaseTabControl.cs" />
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
<Compile Include="Tests\TestCaseTextAwesome.cs" /> <Compile Include="Tests\TestCaseTextAwesome.cs" />
<Compile Include="Tests\TestCasePlaySongSelect.cs" /> <Compile Include="Tests\TestCasePlaySongSelect.cs" />
<Compile Include="Tests\TestCaseTwoLayerButton.cs" /> <Compile Include="Tests\TestCaseTwoLayerButton.cs" />
@ -204,9 +205,15 @@
<Compile Include="Tests\TestCaseModSelectOverlay.cs" /> <Compile Include="Tests\TestCaseModSelectOverlay.cs" />
<Compile Include="Tests\TestCaseDialogOverlay.cs" /> <Compile Include="Tests\TestCaseDialogOverlay.cs" />
<Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" /> <Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" />
<Compile Include="Tests\TestCaseLeaderboard.cs" />
<Compile Include="Beatmaps\TestWorkingBeatmap.cs" />
<Compile Include="Tests\TestCaseBeatmapDetailArea.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup /> <ItemGroup />
<ItemGroup>
<Folder Include="Beatmaps\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,26 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Transforms;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
using OpenTK;
using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement<OsuJudgement>
{
public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement)
{
}
protected override void LoadComplete()
{
if (Judgement.Result != HitResult.Miss)
JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
base.LoadComplete();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
public class NumberPiece : Container public class NumberPiece : Container
{ {
private SpriteText number; private readonly SpriteText number;
public string Text public string Text
{ {
@ -29,6 +29,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
new CircularContainer new CircularContainer
{ {
Masking = true,
Origin = Anchor.Centre,
EdgeEffect = new EdgeEffect EdgeEffect = new EdgeEffect
{ {
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,

View File

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

View File

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

View File

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

View File

@ -33,6 +33,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
completeColour = colours.YellowLight.Opacity(0.8f); completeColour = colours.YellowLight.Opacity(0.8f);
Masking = true;
} }
private class SpinnerBorder : Container private class SpinnerBorder : Container
@ -61,6 +62,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
Colour = Color4.White, Colour = Color4.White,
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
Masking = true,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000), Size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000),
Children = new[] Children = new[]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Modes.Taiko.Judgements namespace osu.Game.Modes.Taiko.Judgements
{ {
public enum TaikoHitResult public enum TaikoHitResult
{ {
[Description("GOOD")]
Good, Good,
[Description("GREAT")]
Great Great
} }
} }

View File

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

View File

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

View File

@ -1,40 +1,32 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
public class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgementInfo> public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgement>
{ {
/// <summary> protected DrawableTaikoHitObject(TaikoHitObject hitObject)
/// The colour used for various elements of this DrawableHitObject.
/// </summary>
public virtual Color4 AccentColour { get; }
public DrawableTaikoHitObject(TaikoHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre; Origin = Anchor.Centre;
RelativePositionAxes = Axes.X; RelativePositionAxes = Axes.X;
}
protected override void LoadComplete()
{
LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2;
LifetimeEnd = HitObject.StartTime + HitObject.PreEmpt; LifetimeEnd = HitObject.StartTime + HitObject.PreEmpt;
// Todo: Remove (suppresses Resharper) base.LoadComplete();
AccentColour = Color4.White;
} }
protected override TaikoJudgementInfo CreateJudgementInfo() => new TaikoJudgementInfo(); protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{
}
/// <summary> /// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between /// Sets the scroll position of the DrawableHitObject relative to the offset between

View File

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

View File

@ -0,0 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Modes.Taiko.Objects
{
public class DrumRollTick : TaikoHitObject
{
/// <summary>
/// Whether this is the first (initial) tick of the slider.
/// </summary>
public bool FirstTick;
/// <summary>
/// The distance between this tick and the next in milliseconds.
/// <para>Half of this value is the hit window of the tick.</para>
/// </summary>
public double TickTimeDistance;
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
namespace osu.Game.Modes.Taiko.Objects
{
public class Hit : TaikoHitObject
{
/// <summary>
/// The hit window that results in a "GREAT" hit.
/// </summary>
public double HitWindowGreat = 35;
/// <summary>
/// The hit window that results in a "GOOD" hit.
/// </summary>
public double HitWindowGood = 80;
/// <summary>
/// The hit window that results in a "MISS".
/// </summary>
public double HitWindowMiss = 95;
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,57 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Objects.Drawables;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Taiko.UI
{
/// <summary>
/// Text that is shown as judgement when a hit object is hit or missed.
/// </summary>
public class DrawableTaikoJudgement : DrawableJudgement<TaikoJudgement>
{
/// <summary>
/// Creates a new judgement text.
/// </summary>
/// <param name="judgement">The judgement to visualise.</param>
public DrawableTaikoJudgement(TaikoJudgement judgement)
: base(judgement)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
switch (Judgement.Result)
{
case HitResult.Hit:
switch (Judgement.TaikoResult)
{
case TaikoHitResult.Good:
Colour = colours.GreenLight;
break;
case TaikoHitResult.Great:
Colour = colours.BlueLight;
break;
}
break;
}
}
protected override void LoadComplete()
{
switch (Judgement.Result)
{
case HitResult.Hit:
MoveToY(-100, 500);
break;
}
base.LoadComplete();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,14 +4,11 @@
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Mods; using osu.Game.Modes.Mods;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -27,14 +24,12 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public PlayMode? PreferredPlayMode; public PlayMode? PreferredPlayMode;
public PlayMode PlayMode => beatmap?.BeatmapInfo?.Mode > PlayMode.Osu ? beatmap.BeatmapInfo.Mode : PreferredPlayMode ?? PlayMode.Osu; public PlayMode PlayMode => Beatmap?.BeatmapInfo?.Mode > PlayMode.Osu ? Beatmap.BeatmapInfo.Mode : PreferredPlayMode ?? PlayMode.Osu;
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(); public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>();
public readonly bool WithStoryboard; public readonly bool WithStoryboard;
protected abstract ArchiveReader GetReader();
protected WorkingBeatmap(BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false) protected WorkingBeatmap(BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false)
{ {
BeatmapInfo = beatmapInfo; BeatmapInfo = beatmapInfo;
@ -42,116 +37,63 @@ namespace osu.Game.Beatmaps
WithStoryboard = withStoryboard; WithStoryboard = withStoryboard;
} }
private Texture background; protected abstract Beatmap GetBeatmap();
private object backgroundLock = new object(); protected abstract Texture GetBackground();
public Texture Background protected abstract Track GetTrack();
{
get
{
lock (backgroundLock)
{
if (background != null) return background;
if (BeatmapInfo?.Metadata?.BackgroundFile == null) return null;
try
{
using (var reader = GetReader())
background = new TextureStore(new RawTextureLoaderStore(reader), false).Get(BeatmapInfo.Metadata.BackgroundFile);
}
catch { }
return background;
}
}
set { lock (backgroundLock) background = value; }
}
private Beatmap beatmap; private Beatmap beatmap;
private object beatmapLock = new object(); private readonly object beatmapLock = new object();
public Beatmap Beatmap public Beatmap Beatmap
{ {
get get
{ {
lock (beatmapLock) lock (beatmapLock)
{ {
if (beatmap != null) return beatmap; return beatmap ?? (beatmap = GetBeatmap());
}
try }
{ }
using (var reader = GetReader())
{ private readonly object backgroundLock = new object();
BeatmapDecoder decoder; private Texture background;
using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path))) public Texture Background
{ {
decoder = BeatmapDecoder.GetDecoder(stream); get
beatmap = decoder?.Decode(stream); {
} lock (backgroundLock)
{
if (WithStoryboard && beatmap != null && BeatmapSetInfo.StoryboardFile != null) return background ?? (background = GetBackground());
using (var stream = new StreamReader(reader.GetStream(BeatmapSetInfo.StoryboardFile)))
decoder?.Decode(stream, beatmap);
}
}
catch { }
return beatmap;
} }
} }
set { lock (beatmapLock) beatmap = value; }
} }
private ArchiveReader trackReader;
private Track track; private Track track;
private object trackLock = new object(); private readonly object trackLock = new object();
public Track Track public Track Track
{ {
get get
{ {
lock (trackLock) lock (trackLock)
{ {
if (track != null) return track; return track ?? (track = GetTrack());
try
{
//store a reference to the reader as we may continue accessing the stream in the background.
trackReader = GetReader();
var trackData = trackReader?.GetStream(BeatmapInfo.Metadata.AudioFile);
if (trackData != null)
track = new TrackBass(trackData);
}
catch { }
return track;
} }
} }
set { lock (trackLock) track = value; }
} }
public bool TrackLoaded => track != null; public bool TrackLoaded => track != null;
private bool isDisposed; public void TransferTo(WorkingBeatmap other)
protected virtual void Dispose(bool disposing)
{ {
if (!isDisposed) if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
{ other.track = track;
track?.Dispose();
background?.Dispose();
isDisposed = true;
}
} }
public void Dispose() public virtual void Dispose()
{ {
Dispose(true); track?.Dispose();
GC.SuppressFinalize(this); track = null;
} background?.Dispose();
background = null;
public void TransferTo(WorkingBeatmap working)
{
if (track != null && BeatmapInfo.AudioEquals(working.BeatmapInfo))
working.track = track;
} }
} }
} }

View File

@ -23,7 +23,8 @@ namespace osu.Game.Configuration
Set(OsuConfig.SavePassword, false); Set(OsuConfig.SavePassword, false);
Set(OsuConfig.SaveUsername, true); Set(OsuConfig.SaveUsername, true);
Set(OsuConfig.CursorSize, 1.0, 0.5f, 2); Set(OsuConfig.MenuCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.GameplayCursorSize, 1.0, 0.5f, 2);
Set(OsuConfig.DimLevel, 30, 0, 100); Set(OsuConfig.DimLevel, 30, 0, 100);
Set(OsuConfig.MouseDisableButtons, false); Set(OsuConfig.MouseDisableButtons, false);
@ -175,11 +176,11 @@ namespace osu.Game.Configuration
ConfineMouseMode.Fullscreen : ConfineMouseMode.Never).Disabled = true; ConfineMouseMode.Fullscreen : ConfineMouseMode.Never).Disabled = true;
GetBindable<bool>(OsuConfig.SavePassword).ValueChanged += delegate GetOriginalBindable<bool>(OsuConfig.SavePassword).ValueChanged += delegate
{ {
if (Get<bool>(OsuConfig.SavePassword)) Set(OsuConfig.SaveUsername, true); if (Get<bool>(OsuConfig.SavePassword)) Set(OsuConfig.SaveUsername, true);
}; };
GetBindable<bool>(OsuConfig.SaveUsername).ValueChanged += delegate GetOriginalBindable<bool>(OsuConfig.SaveUsername).ValueChanged += delegate
{ {
if (!Get<bool>(OsuConfig.SaveUsername)) Set(OsuConfig.SavePassword, false); if (!Get<bool>(OsuConfig.SaveUsername)) Set(OsuConfig.SavePassword, false);
}; };
@ -223,7 +224,8 @@ namespace osu.Game.Configuration
ComboFireHeight, ComboFireHeight,
ConfirmExit, ConfirmExit,
AutoSendNowPlaying, AutoSendNowPlaying,
CursorSize, MenuCursorSize,
GameplayCursorSize,
AutomaticCursorSizing, AutomaticCursorSizing,
DimLevel, DimLevel,
Display, Display,

View File

@ -21,7 +21,7 @@ namespace osu.Game.Database
public class BeatmapDatabase public class BeatmapDatabase
{ {
private SQLiteConnection connection { get; } private SQLiteConnection connection { get; }
private Storage storage; private readonly Storage storage;
public event Action<BeatmapSetInfo> BeatmapSetAdded; public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved; public event Action<BeatmapSetInfo> BeatmapSetRemoved;
@ -342,18 +342,5 @@ namespace osu.Game.Database
} }
public bool Exists(BeatmapSetInfo beatmapSet) => storage.Exists(beatmapSet.Path); public bool Exists(BeatmapSetInfo beatmapSet) => storage.Exists(beatmapSet.Path);
private class DatabaseWorkingBeatmap : WorkingBeatmap
{
private readonly BeatmapDatabase database;
public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false)
: base(beatmapInfo, beatmapSetInfo, withStoryboard)
{
this.database = database;
}
protected override ArchiveReader GetReader() => database?.GetReader(BeatmapSetInfo);
}
} }
} }

View File

@ -0,0 +1,73 @@
// 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.IO;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
namespace osu.Game.Database
{
internal class DatabaseWorkingBeatmap : WorkingBeatmap
{
private readonly BeatmapDatabase database;
public DatabaseWorkingBeatmap(BeatmapDatabase database, BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false)
: base(beatmapInfo, beatmapSetInfo, withStoryboard)
{
this.database = database;
}
private ArchiveReader getReader() => database?.GetReader(BeatmapSetInfo);
protected override Beatmap GetBeatmap()
{
try
{
Beatmap beatmap;
using (var reader = getReader())
{
BeatmapDecoder decoder;
using (var stream = new StreamReader(reader.GetStream(BeatmapInfo.Path)))
{
decoder = BeatmapDecoder.GetDecoder(stream);
beatmap = decoder?.Decode(stream);
}
if (WithStoryboard && beatmap != null && BeatmapSetInfo.StoryboardFile != null)
using (var stream = new StreamReader(reader.GetStream(BeatmapSetInfo.StoryboardFile)))
decoder.Decode(stream, beatmap);
}
return beatmap;
}
catch { return null; }
}
protected override Texture GetBackground()
{
if (BeatmapInfo?.Metadata?.BackgroundFile == null)
return null;
try
{
using (var reader = getReader())
return new TextureStore(new RawTextureLoaderStore(reader), false).Get(BeatmapInfo.Metadata.BackgroundFile);
}
catch { return null; }
}
protected override Track GetTrack()
{
try
{
var trackData = getReader()?.GetStream(BeatmapInfo.Metadata.AudioFile);
return trackData == null ? null : new TrackBass(trackData);
}
catch { return null; }
}
}
}

View File

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

View File

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

View File

@ -89,25 +89,24 @@ namespace osu.Game.Graphics.Backgrounds
protected virtual Triangle CreateTriangle() protected virtual Triangle CreateTriangle()
{ {
float stdDev = 0.16f; const float std_dev = 0.16f;
float mean = 0.5f; const float mean = 0.5f;
float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats
float u2 = 1 - RNG.NextSingle(); float u2 = 1 - RNG.NextSingle();
float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1)
var scale = Math.Max(triangleScale * (mean + stdDev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2)
const float size = 100; const float size = 100;
return new Triangle return new EquilateralTriangle
{ {
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
Size = new Vector2(size),
Scale = new Vector2(scale), Scale = new Vector2(scale),
EdgeSmoothness = new Vector2(1), EdgeSmoothness = new Vector2(1),
Colour = GetTriangleShade(), Colour = GetTriangleShade(),
// Scaling height by 0.866 results in equiangular triangles (== 60° and equal side length)
Size = new Vector2(size, 0.866f * size),
Depth = scale, Depth = scale,
}; };
} }

View File

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

View File

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

View File

@ -54,8 +54,6 @@ namespace osu.Game.Graphics.Cursor
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
cursorScale = config.GetBindable<double>(OsuConfig.CursorSize);
Children = new Drawable[] Children = new Drawable[]
{ {
cursorContainer = new CircularContainer cursorContainer = new CircularContainer
@ -63,7 +61,6 @@ namespace osu.Game.Graphics.Cursor
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Scale = new Vector2((float)cursorScale),
Masking = true, Masking = true,
BorderThickness = Size.X / 6, BorderThickness = Size.X / 6,
BorderColour = Color4.White, BorderColour = Color4.White,
@ -119,7 +116,9 @@ namespace osu.Game.Graphics.Cursor
}, },
}; };
cursorScale = config.GetBindable<double>(OsuConfig.GameplayCursorSize);
cursorScale.ValueChanged += scaleChanged; cursorScale.ValueChanged += scaleChanged;
cursorScale.TriggerChange();
} }
private void scaleChanged(object sender, EventArgs e) private void scaleChanged(object sender, EventArgs e)

View File

@ -97,8 +97,6 @@ namespace osu.Game.Graphics.Cursor
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, TextureStore textures, OsuColour colour) private void load(OsuConfigManager config, TextureStore textures, OsuColour colour)
{ {
cursorScale = config.GetBindable<double>(OsuConfig.CursorSize);
Children = new Drawable[] Children = new Drawable[]
{ {
cursorContainer = new Container cursorContainer = new Container
@ -122,7 +120,10 @@ namespace osu.Game.Graphics.Cursor
} }
} }
}; };
cursorScale = config.GetBindable<double>(OsuConfig.MenuCursorSize);
cursorScale.ValueChanged += scaleChanged; cursorScale.ValueChanged += scaleChanged;
cursorScale.TriggerChange();
} }
private void scaleChanged(object sender, EventArgs e) private void scaleChanged(object sender, EventArgs e)

View File

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

View File

@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface
public const float COLLAPSED_SIZE = 20; public const float COLLAPSED_SIZE = 20;
public const float EXPANDED_SIZE = 40; public const float EXPANDED_SIZE = 40;
private Box fill; private readonly Box fill;
private const float border_width = 3; private const float border_width = 3;
private Color4 glowingColour, idleColour; private Color4 glowingColour, idleColour;
@ -30,6 +30,8 @@ namespace osu.Game.Graphics.UserInterface
BorderColour = Color4.White; BorderColour = Color4.White;
BorderThickness = border_width; BorderThickness = border_width;
Masking = true;
Children = new[] Children = new[]
{ {
fill = new Box fill = new Box

View File

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

View File

@ -1,69 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Graphics.UserInterface
{
public class OsuDropDownHeader : DropDownHeader
{
private SpriteText label;
protected override string Label
{
get { return label.Text; }
set { label.Text = value; }
}
private Color4? accentColour;
public virtual Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
set
{
accentColour = value;
BackgroundColourHover = value;
}
}
public OsuDropDownHeader()
{
Foreground.Padding = new MarginPadding(4);
AutoSizeAxes = Axes.None;
Margin = new MarginPadding { Bottom = 4 };
CornerRadius = 4;
Height = 40;
Foreground.Children = new Drawable[]
{
label = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new TextAwesome
{
Icon = FontAwesome.fa_chevron_down,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 4 },
TextSize = 20
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = Color4.Black.Opacity(0.5f);
BackgroundColourHover = accentColour ?? colours.PinkDarker;
}
}
}

View File

@ -1,61 +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.Linq;
using osu.Framework.Allocation;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Graphics.UserInterface
{
public class OsuDropDownMenu<T> : DropDownMenu<T>
{
protected override DropDownHeader CreateHeader() => new OsuDropDownHeader { AccentColour = AccentColour };
private Color4? accentColour;
public virtual Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
set
{
accentColour = value;
if (Header != null)
((OsuDropDownHeader)Header).AccentColour = value;
foreach (var item in ItemList.OfType<OsuDropDownMenuItem<T>>())
item.AccentColour = value;
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (accentColour == null)
AccentColour = colours.PinkDarker;
}
public OsuDropDownMenu()
{
ContentContainer.CornerRadius = 4;
ContentBackground.Colour = Color4.Black.Opacity(0.5f);
DropDownItemsContainer.Padding = new MarginPadding(5);
}
protected override void AnimateOpen() => ContentContainer.FadeIn(300, EasingTypes.OutQuint);
protected override void AnimateClose() => ContentContainer.FadeOut(300, EasingTypes.OutQuint);
protected override void UpdateContentHeight()
{
var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight;
ContentContainer.ResizeTo(new Vector2(1, State == DropDownMenuState.Opened ? actualHeight : 0), 300, EasingTypes.OutQuint);
}
protected override DropDownMenuItem<T> CreateDropDownItem(string key, T value) => new OsuDropDownMenuItem<T>(key, value) { AccentColour = AccentColour };
}
}

View File

@ -1,85 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public class OsuDropDownMenuItem<U> : DropDownMenuItem<U>
{
public OsuDropDownMenuItem(string text, U value) : base(text, value)
{
Foreground.Padding = new MarginPadding(2);
Masking = true;
CornerRadius = 6;
Children = new[]
{
new FillFlowContainer
{
Direction = FillDirection.Horizontal,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
chevron = new TextAwesome
{
AlwaysPresent = true,
Icon = FontAwesome.fa_chevron_right,
UseFullGlyphHeight = false,
Colour = Color4.Black,
Alpha = 0.5f,
TextSize = 8,
Margin = new MarginPadding { Left = 3, Right = 3 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
new OsuSpriteText {
Text = text,
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
}
}
}
};
}
private Color4? accentColour;
private TextAwesome chevron;
protected override void FormatForeground(bool hover = false)
{
base.FormatForeground(hover);
chevron.Alpha = hover ? 1 : 0;
}
public Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
set
{
accentColour = value;
BackgroundColourHover = BackgroundColourSelected = value;
FormatBackground();
FormatForeground();
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = Color4.Transparent;
BackgroundColourHover = accentColour ?? colours.PinkDarker;
BackgroundColourSelected = Color4.Black.Opacity(0.5f);
}
}
}

View File

@ -0,0 +1,172 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
{
public class OsuDropdown<T> : Dropdown<T>
{
protected override DropdownHeader CreateHeader() => new OsuDropdownHeader { AccentColour = AccentColour };
protected override Menu CreateMenu() => new OsuMenu();
private Color4? accentColour;
public virtual Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
set
{
accentColour = value;
if (Header != null)
((OsuDropdownHeader)Header).AccentColour = value;
foreach (var item in MenuItems.OfType<OsuDropdownMenuItem>())
item.AccentColour = value;
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
if (accentColour == null)
AccentColour = colours.PinkDarker;
}
protected override DropdownMenuItem<T> CreateMenuItem(string text, T value) => new OsuDropdownMenuItem(text, value) { AccentColour = AccentColour };
private class OsuDropdownMenuItem : DropdownMenuItem<T>
{
public OsuDropdownMenuItem(string text, T value) : base(text, value)
{
Foreground.Padding = new MarginPadding(2);
Masking = true;
CornerRadius = 6;
Children = new[]
{
new FillFlowContainer
{
Direction = FillDirection.Horizontal,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
chevron = new TextAwesome
{
AlwaysPresent = true,
Icon = FontAwesome.fa_chevron_right,
UseFullGlyphHeight = false,
Colour = Color4.Black,
Alpha = 0.5f,
TextSize = 8,
Margin = new MarginPadding { Left = 3, Right = 3 },
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
},
new OsuSpriteText {
Text = text,
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
}
}
}
};
}
private Color4? accentColour;
private readonly TextAwesome chevron;
protected override void FormatForeground(bool hover = false)
{
base.FormatForeground(hover);
chevron.Alpha = hover ? 1 : 0;
}
public Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
set
{
accentColour = value;
BackgroundColourHover = BackgroundColourSelected = value;
FormatBackground();
FormatForeground();
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = Color4.Transparent;
BackgroundColourHover = accentColour ?? colours.PinkDarker;
BackgroundColourSelected = Color4.Black.Opacity(0.5f);
}
}
protected class OsuDropdownHeader : DropdownHeader
{
private readonly SpriteText label;
protected override string Label
{
get { return label.Text; }
set { label.Text = value; }
}
private Color4? accentColour;
public virtual Color4 AccentColour
{
get { return accentColour.GetValueOrDefault(); }
set
{
accentColour = value;
BackgroundColourHover = value;
}
}
public OsuDropdownHeader()
{
Foreground.Padding = new MarginPadding(4);
AutoSizeAxes = Axes.None;
Margin = new MarginPadding { Bottom = 4 };
CornerRadius = 4;
Height = 40;
Foreground.Children = new Drawable[]
{
label = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
new TextAwesome
{
Icon = FontAwesome.fa_chevron_down,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 4 },
TextSize = 20
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = Color4.Black.Opacity(0.5f);
BackgroundColourHover = accentColour ?? colours.PinkDarker;
}
}
}
}

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 OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface;
namespace osu.Game.Graphics.UserInterface
{
public class OsuMenu : Menu
{
public OsuMenu()
{
CornerRadius = 4;
Background.Colour = Color4.Black.Opacity(0.5f);
ItemsContainer.Padding = new MarginPadding(5);
}
protected override void AnimateOpen() => FadeIn(300, EasingTypes.OutQuint);
protected override void AnimateClose() => FadeOut(300, EasingTypes.OutQuint);
protected override void UpdateContentHeight()
{
var actualHeight = (RelativeSizeAxes & Axes.Y) > 0 ? 1 : ContentHeight;
ResizeTo(new Vector2(1, State == MenuState.Opened ? actualHeight : 0), 300, EasingTypes.OutQuint);
}
}
}

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