This commit is contained in:
DrabWeb
2017-03-13 09:36:16 -03:00
286 changed files with 3653 additions and 2140 deletions

View File

@ -1,23 +1,44 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Modes.Objects;
using osu.Game.Modes;
using osu.Game.Modes.Objects;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Beatmaps
{
public class Beatmap
/// <summary>
/// A Beatmap containing converted HitObjects.
/// </summary>
public class Beatmap<T>
where T : HitObject
{
public BeatmapInfo BeatmapInfo { get; set; }
public BeatmapInfo BeatmapInfo;
public List<ControlPoint> ControlPoints;
public List<Color4> ComboColors;
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata;
public List<HitObject> HitObjects { get; set; }
public List<ControlPoint> ControlPoints { get; set; }
public List<Color4> ComboColors { get; set; }
/// <summary>
/// The HitObjects this Beatmap contains.
/// </summary>
public List<T> HitObjects;
/// <summary>
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null)
{
BeatmapInfo = original?.BeatmapInfo;
ControlPoints = original?.ControlPoints;
ComboColors = original?.ComboColors;
}
public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength;
public double BPMMinimum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength;
public double BPMMode => BPMAt(ControlPoints.Where(c => c.BeatLength != 0).GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).First().First().Time);
@ -58,7 +79,17 @@ namespace osu.Game.Beatmaps
return timingPoint ?? ControlPoint.Default;
}
}
/// <summary>
/// A Beatmap containing un-converted HitObjects.
/// </summary>
public class Beatmap : Beatmap<HitObject>
{
/// <summary>
/// Calculates the star difficulty for this Beatmap.
/// </summary>
/// <returns>The star difficulty.</returns>
public double CalculateStarDifficulty() => Ruleset.GetRuleset(BeatmapInfo.Mode).CreateDifficultyCalculator(this).Calculate();
}
}

View File

@ -1,20 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes;
using osu.Game.Modes.Objects;
using System;
using System.Collections.Generic;
namespace osu.Game.Beatmaps
{
public abstract class DifficultyCalculator
{
protected abstract PlayMode PlayMode { get; }
protected double TimeRate = 1;
protected abstract double CalculateInternal(Dictionary<String, String> categoryDifficulty);
protected abstract double CalculateInternal(Dictionary<string, string> categoryDifficulty);
private void loadTiming()
{
@ -35,16 +31,16 @@ namespace osu.Game.Beatmaps
{
protected List<T> Objects;
protected abstract HitObjectConverter<T> Converter { get; }
public DifficultyCalculator(Beatmap beatmap)
protected DifficultyCalculator(Beatmap beatmap)
{
Objects = Converter.Convert(beatmap);
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
PreprocessHitObjects();
}
protected virtual void PreprocessHitObjects()
{
}
protected abstract IBeatmapConverter<T> CreateBeatmapConverter();
}
}

View File

@ -6,7 +6,7 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables
{
class BeatmapBackgroundSprite : Sprite
internal class BeatmapBackgroundSprite : Sprite
{
private readonly WorkingBeatmap working;

View File

@ -10,7 +10,7 @@ using osu.Game.Database;
namespace osu.Game.Beatmaps.Drawables
{
class BeatmapGroup : IStateful<BeatmapGroupState>
internal class BeatmapGroup : IStateful<BeatmapGroupState>
{
public BeatmapPanel SelectedPanel;

View File

@ -18,7 +18,7 @@ using osu.Game.Graphics.Sprites;
namespace osu.Game.Beatmaps.Drawables
{
class BeatmapPanel : Panel
internal class BeatmapPanel : Panel
{
public BeatmapInfo Beatmap;
private Sprite background;

View File

@ -17,7 +17,7 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
class BeatmapSetHeader : Panel
internal class BeatmapSetHeader : Panel
{
public Action<BeatmapSetHeader> GainedSelection;
private SpriteText title, artist;
@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps.Drawables
base.Dispose(isDisposing);
}
class PanelBackground : BufferedContainer
private class PanelBackground : BufferedContainer
{
private readonly WorkingBeatmap working;
@ -160,7 +160,7 @@ namespace osu.Game.Beatmaps.Drawables
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
}.LoadAsync(game, (bg) =>
}.LoadAsync(game, bg =>
{
Add(bg);
ForceRedraw();

View File

@ -12,7 +12,7 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
class DifficultyIcon : Container
internal class DifficultyIcon : Container
{
private readonly BeatmapInfo beatmap;
private OsuColour palette;
@ -34,6 +34,7 @@ namespace osu.Game.Beatmaps.Drawables
new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = Size.X,
Colour = getColour(beatmap),
Icon = FontAwesome.fa_circle
@ -41,6 +42,7 @@ namespace osu.Game.Beatmaps.Drawables
new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = Size.X,
Colour = Color4.White,
Icon = Ruleset.GetRuleset(beatmap.Mode).Icon
@ -48,7 +50,7 @@ namespace osu.Game.Beatmaps.Drawables
};
}
enum DifficultyRating
private enum DifficultyRating
{
Easy,
Normal,

View File

@ -8,11 +8,11 @@ using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Beatmaps.Drawables
{
class Panel : Container, IStateful<PanelSelectedState>
internal class Panel : Container, IStateful<PanelSelectedState>
{
public const float MAX_HEIGHT = 80;
@ -115,7 +115,7 @@ namespace osu.Game.Beatmaps.Drawables
}
}
enum PanelSelectedState
internal enum PanelSelectedState
{
Hidden,
NotSelected,

View File

@ -17,8 +17,9 @@ namespace osu.Game.Beatmaps.Formats
public static BeatmapDecoder GetDecoder(TextReader stream)
{
var line = stream.ReadLine().Trim();
if (!decoders.ContainsKey(line))
var line = stream.ReadLine()?.Trim();
if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format");
return (BeatmapDecoder)Activator.CreateInstance(decoders[line]);
}

View File

@ -197,7 +197,7 @@ namespace osu.Game.Beatmaps.Formats
if (split.Length > 2)
{
int kiaiFlags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0;
//int kiaiFlags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0;
double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
cp = new ControlPoint
{
@ -219,15 +219,18 @@ namespace osu.Game.Beatmaps.Formats
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {val}");
byte r, g, b;
if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
throw new InvalidOperationException($@"Color must be specified with 8-bit integer components");
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
// Note: the combo index specified in the beatmap is discarded
beatmap.ComboColors.Add(new Color4
if (key.StartsWith(@"Combo"))
{
R = r / 255f,
G = g / 255f,
B = b / 255f,
A = 1f,
});
beatmap.ComboColors.Add(new Color4
{
R = r / 255f,
G = g / 255f,
B = b / 255f,
A = 1f,
});
}
}
protected override void ParseFile(TextReader stream, Beatmap beatmap)
@ -235,10 +238,9 @@ namespace osu.Game.Beatmaps.Formats
HitObjectParser parser = null;
var section = Section.None;
string line;
while (true)
{
line = stream.ReadLine();
var line = stream.ReadLine();
if (line == null)
break;
if (string.IsNullOrEmpty(line))

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Objects;
namespace osu.Game.Beatmaps
{
public interface IBeatmapConverter<T> where T : HitObject
{
Beatmap<T> Convert(Beatmap original);
}
}

View File

@ -0,0 +1,44 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IO;
namespace osu.Game.Beatmaps.IO
{
public abstract class BeatmapArchiveReader : ArchiveReader
{
public const string OSZ_EXTENSION = @".osz";
public static BeatmapArchiveReader GetBeatmapArchiveReader(Storage storage, string path)
{
try
{
return (BeatmapArchiveReader)GetReader(storage, path);
}
catch (InvalidCastException e)
{
Logger.Error(e, "A tricky " + $@"{nameof(ArchiveReader)}" + " instance passed the test to be a " + $@"{nameof(BeatmapArchiveReader)}" + ", but it's really not");
throw;
}
}
/// <summary>
/// Reads the beatmap metadata from this archive.
/// </summary>
public abstract BeatmapMetadata ReadMetadata();
/// <summary>
/// Gets a list of beatmap file names.
/// </summary>
public string[] BeatmapFilenames { get; protected set; }
/// <summary>
/// The storyboard filename. Null if no storyboard is present.
/// </summary>
public string StoryboardFilename { get; protected set; }
}
}

View File

@ -9,14 +9,14 @@ using osu.Game.Database;
namespace osu.Game.Beatmaps.IO
{
public sealed class OszArchiveReader : ArchiveReader
public sealed class OszArchiveReader : BeatmapArchiveReader
{
public static void Register()
{
AddReader<OszArchiveReader>((storage, path) =>
{
using (var stream = storage.GetStream(path))
return ZipFile.IsZipFile(stream, false);
return Path.GetExtension(path) == OSZ_EXTENSION && ZipFile.IsZipFile(stream, false);
});
OsuLegacyDecoder.Register();
}

View File

@ -15,7 +15,8 @@ namespace osu.Game.Beatmaps.Timing
public double BeatLength;
public double VelocityAdjustment;
public bool TimingChange;
public bool KiaiMode;
}
internal enum TimeSignatures

View File

@ -3,7 +3,7 @@
namespace osu.Game.Beatmaps.Timing
{
class TimingChange : ControlPoint
internal class TimingChange : ControlPoint
{
public TimingChange(double beatLength)
{

View File

@ -2,12 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.IO;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO;
using osu.Game.Database;
using osu.Game.Modes;
namespace osu.Game.Beatmaps
{
@ -17,9 +20,19 @@ namespace osu.Game.Beatmaps
public readonly BeatmapSetInfo BeatmapSetInfo;
/// <summary>
/// A play mode that is preferred for this beatmap. PlayMode will become this mode where conversion is feasible,
/// or otherwise to the beatmap's default.
/// </summary>
public PlayMode? PreferredPlayMode;
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 bool WithStoryboard;
protected abstract ArchiveReader GetReader();
protected abstract BeatmapArchiveReader GetReader();
protected WorkingBeatmap(BeatmapInfo beatmapInfo, BeatmapSetInfo beatmapSetInfo, bool withStoryboard = false)
{
@ -87,7 +100,7 @@ namespace osu.Game.Beatmaps
set { lock (beatmapLock) beatmap = value; }
}
private ArchiveReader trackReader;
private BeatmapArchiveReader trackReader;
private Track track;
private object trackLock = new object();
public Track Track

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Cryptography;
using osu.Framework.Extensions;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
@ -20,19 +20,20 @@ namespace osu.Game.Database
{
public class BeatmapDatabase
{
private SQLiteConnection connection { get; set; }
private SQLiteConnection connection { get; }
private Storage storage;
public event Action<BeatmapSetInfo> BeatmapSetAdded;
public event Action<BeatmapSetInfo> BeatmapSetRemoved;
private BeatmapImporter ipc;
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private BeatmapIPCChannel ipc;
public BeatmapDatabase(Storage storage, GameHost importHost = null)
public BeatmapDatabase(Storage storage, IIpcHost importHost = null)
{
this.storage = storage;
if (importHost != null)
ipc = new BeatmapImporter(importHost, this);
ipc = new BeatmapIPCChannel(importHost, this);
if (connection == null)
{
@ -73,7 +74,7 @@ namespace osu.Game.Database
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete beatmap {b.ToString()}");
Logger.Error(e, $@"Could not delete beatmap {b}");
}
}
@ -149,9 +150,9 @@ namespace osu.Game.Database
catch (Exception e)
{
e = e.InnerException ?? e;
Logger.Error(e, $@"Could not import beatmap set");
Logger.Error(e, @"Could not import beatmap set");
}
// Batch commit with multiple sets to database
Import(sets);
}
@ -162,7 +163,7 @@ namespace osu.Game.Database
/// <param name="path">Location on disk</param>
public void Import(string path)
{
Import(new [] { path });
Import(new[] { path });
}
/// <summary>
@ -176,17 +177,16 @@ namespace osu.Game.Database
BeatmapMetadata metadata;
using (var reader = ArchiveReader.GetReader(storage, path))
using (var reader = BeatmapArchiveReader.GetBeatmapArchiveReader(storage, path))
metadata = reader.ReadMetadata();
if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader
{
using (var md5 = MD5.Create())
using (var input = storage.GetStream(path))
{
hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant();
hash = input.GetMd5Hash();
input.Seek(0, SeekOrigin.Begin);
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash);
path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash + BeatmapArchiveReader.OSZ_EXTENSION);
if (!storage.Exists(path))
using (var output = storage.GetStream(path, FileAccess.Write))
input.CopyTo(output);
@ -216,22 +216,29 @@ namespace osu.Game.Database
Metadata = metadata
};
using (var reader = ArchiveReader.GetReader(storage, path))
using (var archive = BeatmapArchiveReader.GetBeatmapArchiveReader(storage, path))
{
string[] mapNames = reader.BeatmapFilenames;
string[] mapNames = archive.BeatmapFilenames;
foreach (var name in mapNames)
using (var stream = new StreamReader(reader.GetStream(name)))
using (var raw = archive.GetStream(name))
using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit
using (var sr = new StreamReader(ms))
{
var decoder = BeatmapDecoder.GetDecoder(stream);
Beatmap beatmap = decoder.Decode(stream);
raw.CopyTo(ms);
ms.Position = 0;
var decoder = BeatmapDecoder.GetDecoder(sr);
Beatmap beatmap = decoder.Decode(sr);
beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.GetMd5Hash();
// TODO: Diff beatmap metadata with set metadata and leave it here if necessary
beatmap.BeatmapInfo.Metadata = null;
beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo);
}
beatmapSet.StoryboardFile = reader.StoryboardFilename;
beatmapSet.StoryboardFile = archive.StoryboardFilename;
}
return beatmapSet;
@ -261,12 +268,12 @@ namespace osu.Game.Database
BeatmapSetRemoved?.Invoke(beatmapSet);
}
public ArchiveReader GetReader(BeatmapSetInfo beatmapSet)
public BeatmapArchiveReader GetReader(BeatmapSetInfo beatmapSet)
{
if (string.IsNullOrEmpty(beatmapSet.Path))
return null;
return ArchiveReader.GetReader(storage, beatmapSet.Path);
return BeatmapArchiveReader.GetBeatmapArchiveReader(storage, beatmapSet.Path);
}
public BeatmapSetInfo GetBeatmapSet(int id)
@ -318,8 +325,7 @@ namespace osu.Game.Database
return item;
}
readonly Type[] validTypes = new[]
{
private readonly Type[] validTypes = {
typeof(BeatmapSetInfo),
typeof(BeatmapInfo),
typeof(BeatmapMetadata),
@ -329,7 +335,7 @@ namespace osu.Game.Database
public void Update<T>(T record, bool cascade = true) where T : class
{
if (validTypes.All(t => t != typeof(T)))
throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase");
throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T));
if (cascade)
connection.UpdateWithChildren(record);
else
@ -348,7 +354,7 @@ namespace osu.Game.Database
this.database = database;
}
protected override ArchiveReader GetReader() => database?.GetReader(BeatmapSetInfo);
protected override BeatmapArchiveReader GetReader() => database?.GetReader(BeatmapSetInfo);
}
}
}

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes;
using SQLite.Net.Attributes;
using SQLiteNetExtensions.Attributes;
using System;
using System.Linq;
namespace osu.Game.Database
{
@ -15,9 +15,9 @@ namespace osu.Game.Database
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public int? OnlineBeatmapID { get; set; } = null;
public int? OnlineBeatmapID { get; set; }
public int? OnlineBeatmapSetID { get; set; } = null;
public int? OnlineBeatmapSetID { get; set; }
[ForeignKey(typeof(BeatmapSetInfo))]
public int BeatmapSetInfoID { get; set; }
@ -39,6 +39,8 @@ namespace osu.Game.Database
public string Path { get; set; }
public string Hash { get; set; }
// General
public int AudioLeadIn { get; set; }
public bool Countdown { get; set; }
@ -57,13 +59,14 @@ namespace osu.Game.Database
{
get
{
return StoredBookmarks.Split(',').Select(b => int.Parse(b)).ToArray();
return StoredBookmarks.Split(',').Select(int.Parse).ToArray();
}
set
{
StoredBookmarks = string.Join(",", value);
}
}
public double DistanceSpacing { get; set; }
public int BeatDivisor { get; set; }
public int GridSize { get; set; }
@ -77,9 +80,9 @@ namespace osu.Game.Database
{
get
{
return (starDifficulty < 0) ? (BaseDifficulty?.OverallDifficulty ?? 5) : starDifficulty;
return starDifficulty < 0 ? (BaseDifficulty?.OverallDifficulty ?? 5) : starDifficulty;
}
set { starDifficulty = value; }
}

View File

@ -10,7 +10,7 @@ namespace osu.Game.Database
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public int? OnlineBeatmapSetID { get; set; } = null;
public int? OnlineBeatmapSetID { get; set; }
public string Title { get; set; }
public string TitleUnicode { get; set; }

View File

@ -12,7 +12,7 @@ namespace osu.Game.Database
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public int? OnlineBeatmapSetID { get; set; } = null;
public int? OnlineBeatmapSetID { get; set; }
[OneToOne(CascadeOperations = CascadeOperation.All)]
public BeatmapMetadata Metadata { get; set; }

View File

@ -0,0 +1,112 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO;
using System.Linq;
using osu.Framework.Platform;
using osu.Game.IO.Legacy;
using osu.Game.IPC;
using osu.Game.Modes;
using SharpCompress.Compressors.LZMA;
namespace osu.Game.Database
{
public class ScoreDatabase
{
private readonly Storage storage;
private readonly BeatmapDatabase beatmaps;
private const string replay_folder = @"replays";
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
private ScoreIPCChannel ipc;
public ScoreDatabase(Storage storage, IIpcHost importHost = null, BeatmapDatabase beatmaps = null)
{
this.storage = storage;
this.beatmaps = beatmaps;
if (importHost != null)
ipc = new ScoreIPCChannel(importHost, this);
}
public Score ReadReplayFile(string replayFilename)
{
Score score;
using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename)))
using (SerializationReader sr = new SerializationReader(s))
{
var ruleset = Ruleset.GetRuleset((PlayMode)sr.ReadByte());
var processor = ruleset.CreateScoreProcessor();
score = processor.GetScore();
/* score.Pass = true;*/
var version = sr.ReadInt32();
/* score.FileChecksum = */
var beatmapHash = sr.ReadString();
score.Beatmap = beatmaps.Query<BeatmapInfo>().FirstOrDefault(b => b.Hash == beatmapHash);
/* score.PlayerName = */
sr.ReadString();
/* var localScoreChecksum = */
sr.ReadString();
/* score.Count300 = */
sr.ReadUInt16();
/* score.Count100 = */
sr.ReadUInt16();
/* score.Count50 = */
sr.ReadUInt16();
/* score.CountGeki = */
sr.ReadUInt16();
/* score.CountKatu = */
sr.ReadUInt16();
/* score.CountMiss = */
sr.ReadUInt16();
score.TotalScore = sr.ReadInt32();
score.MaxCombo = sr.ReadUInt16();
/* score.Perfect = */
sr.ReadBoolean();
/* score.EnabledMods = (Mods)*/
sr.ReadInt32();
/* score.HpGraphString = */
sr.ReadString();
/* score.Date = */
sr.ReadDateTime();
var compressedReplay = sr.ReadByteArray();
if (version >= 20140721)
/*OnlineId =*/
sr.ReadInt64();
else if (version >= 20121008)
/*OnlineId =*/
sr.ReadInt32();
using (var replayInStream = new MemoryStream(compressedReplay))
{
byte[] properties = new byte[5];
if (replayInStream.Read(properties, 0, 5) != 5)
throw new Exception("input .lzma is too short");
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = replayInStream.ReadByte();
if (v < 0)
throw new Exception("Can't Read 1");
outSize |= (long)(byte)v << (8 * i);
}
long compressedSize = replayInStream.Length - replayInStream.Position;
using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize))
using (var reader = new StreamReader(lzma))
score.Replay = new LegacyReplay(reader);
}
}
return score;
}
}
}

View File

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

View File

@ -117,7 +117,7 @@ namespace osu.Game.Graphics.Backgrounds
private void addTriangle(bool randomY)
{
var sprite = CreateTriangle();
float triangleHeight = (sprite.DrawHeight / DrawHeight);
float triangleHeight = sprite.DrawHeight / DrawHeight;
sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1);
Add(sprite);
}

View File

@ -1,28 +0,0 @@
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Containers;
namespace osu.Game.Graphics.Components
{
class FpsDisplay : OsuComponent
{
SpriteText fpsText;
public override void Load()
{
base.Load();
Add(fpsText = new SpriteText());
fpsText.Text = "...";
}
protected override void Update()
{
fpsText.Text = ((int)(1000 / Clock.ElapsedFrameTime)).ToString();
base.Update();
}
}
}

View File

@ -12,7 +12,7 @@ using osu.Framework.Configuration;
namespace osu.Game.Graphics.Containers
{
class ParallaxContainer : Container
internal class ParallaxContainer : Container
{
public float ParallaxAmount = 0.02f;
@ -51,7 +51,7 @@ namespace osu.Game.Graphics.Containers
};
}
bool firstUpdate = true;
private bool firstUpdate = true;
protected override void Update()
{

View File

@ -16,8 +16,7 @@ using osu.Framework.Graphics.Colour;
namespace osu.Game.Graphics.Cursor
{
class CursorTrail : Drawable
internal class CursorTrail : Drawable
{
public override bool Contains(Vector2 screenSpacePos) => true;
public override bool HandleInput => true;
@ -32,7 +31,7 @@ namespace osu.Game.Graphics.Cursor
private double timeOffset;
private float time;
private TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
private const int max_sprites = 2048;
@ -46,7 +45,7 @@ namespace osu.Game.Graphics.Cursor
{
base.ApplyDrawNode(node);
TrailDrawNode tNode = node as TrailDrawNode;
TrailDrawNode tNode = (TrailDrawNode)node;
tNode.Shader = shader;
tNode.Texture = texture;
tNode.Size = size;
@ -77,6 +76,12 @@ namespace osu.Game.Graphics.Cursor
Scale = new Vector2(1 / texture.ScaleAdjust);
}
protected override void LoadComplete()
{
base.LoadComplete();
resetTime();
}
protected override void Update()
{
base.Update();
@ -117,7 +122,7 @@ namespace osu.Game.Graphics.Cursor
float distance = diff.Length;
Vector2 direction = diff / distance;
float interval = (size.X / 2) * 0.9f;
float interval = size.X / 2 * 0.9f;
for (float d = interval; d < distance; d += interval)
{
@ -137,7 +142,7 @@ namespace osu.Game.Graphics.Cursor
currentIndex = (currentIndex + 1) % max_sprites;
}
struct TrailPart
private struct TrailPart
{
public Vector2 Position;
public float Time;
@ -145,12 +150,12 @@ namespace osu.Game.Graphics.Cursor
public bool WasUpdated;
}
class TrailDrawNodeSharedData
private class TrailDrawNodeSharedData
{
public VertexBuffer<TexturedVertex2D> VertexBuffer;
}
class TrailDrawNode : DrawNode
private class TrailDrawNode : DrawNode
{
public Shader Shader;
public Texture Texture;

View File

@ -5,11 +5,11 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Configuration;
@ -17,7 +17,7 @@ using System;
namespace osu.Game.Graphics.Cursor
{
class OsuCursorContainer : CursorContainer
public class OsuCursorContainer : CursorContainer
{
protected override Drawable CreateCursor() => new OsuCursor();
@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Cursor
return base.OnMouseUp(state, args);
}
class OsuCursor : Container
public class OsuCursor : Container
{
private Container cursorContainer;
private Bindable<double> cursorScale;

View File

@ -6,12 +6,6 @@ using OpenTK.Graphics;
namespace osu.Game.Graphics
{
public static class OsuColourExtensions
{
public static Color4 Opacity(this Color4 color, float a) => new Color4(color.R, color.G, color.B, a);
public static Color4 Opacity(this Color4 color, byte a) => new Color4(color.R, color.G, color.B, a / 255f);
}
public class OsuColour
{
public static Color4 Gray(float amt) => new Color4(amt, amt, amt, 1f);

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics;
namespace osu.Game.Graphics.Processing
{
class RatioAdjust : Container
internal class RatioAdjust : Container
{
public override bool Contains(Vector2 screenSpacePos) => true;

View File

@ -26,11 +26,6 @@ namespace osu.Game.Graphics
Text = ((char)icon).ToString();
}
}
public TextAwesome()
{
Origin = Framework.Graphics.Anchor.Centre;
}
}
public enum FontAwesome

View File

@ -9,9 +9,9 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Containers;
using osu.Framework.Audio.Sample;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Graphics.UserInterface
{
@ -100,7 +100,7 @@ namespace osu.Game.Graphics.UserInterface
Delay(click_duration);
Schedule(delegate {
colourContainer.ResizeTo(new Vector2(0.8f, 1f), 0, EasingTypes.None);
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
spriteText.Spacing = Vector2.Zero;
glowContainer.FadeOut();
});

View File

@ -20,7 +20,7 @@ namespace osu.Game.Graphics.UserInterface
private Box fill;
const float border_width = 3;
private const float border_width = 3;
private Color4 glowingColour, idleColour;
public Nub()

View File

@ -3,6 +3,7 @@
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;

View File

@ -8,6 +8,7 @@ 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
{

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Graphics.UserInterface
{
@ -35,10 +36,7 @@ namespace osu.Game.Graphics.UserInterface
protected override void UpdateContentHeight()
{
if (State == DropDownMenuState.Opened)
ContentContainer.ResizeTo(new Vector2(1, ContentHeight), 300, EasingTypes.OutQuint);
else
ContentContainer.ResizeTo(new Vector2(1, 0), 300, EasingTypes.OutQuint);
ContentContainer.ResizeTo(State == DropDownMenuState.Opened ? new Vector2(1, ContentHeight) : new Vector2(1, 0), 300, EasingTypes.OutQuint);
}
}
}

View File

@ -2,6 +2,7 @@
// 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;

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Graphics.UserInterface
{

View File

@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using System;
namespace osu.Game.Graphics.UserInterface
@ -10,7 +11,7 @@ namespace osu.Game.Graphics.UserInterface
/// <summary>
/// Used as an accuracy counter. Represented visually as a percentage.
/// </summary>
public class PercentageCounter : RollingCounter<float>
public class PercentageCounter : RollingCounter<double>
{
protected override Type TransformType => typeof(TransformAccuracy);
@ -20,36 +21,48 @@ namespace osu.Game.Graphics.UserInterface
public void SetFraction(float numerator, float denominator)
{
Count = Math.Abs(denominator) < epsilon ? 1.0f : numerator / denominator;
Current.Value = Math.Abs(denominator) < epsilon ? 1.0f : numerator / denominator;
}
public PercentageCounter()
{
DisplayedCountSpriteText.FixedWidth = true;
Count = DisplayedCount = 1.0f;
Current.Value = DisplayedCount = 1.0f;
}
protected override string FormatCount(float count)
protected override string FormatCount(double count)
{
return $@"{count:P2}";
}
protected override double GetProportionalDuration(float currentValue, float newValue)
protected override double GetProportionalDuration(double currentValue, double newValue)
{
return Math.Abs(currentValue - newValue) * RollingDuration * 100.0f;
}
public override void Increment(float amount)
public override void Increment(double amount)
{
Count = Count + amount;
Current.Value = Current + amount;
}
protected class TransformAccuracy : TransformFloat
protected class TransformAccuracy : Transform<double>
{
protected override double CurrentValue
{
get
{
double time = Time?.Current ?? 0;
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return Interpolation.ValueAt(time, (float)StartValue, (float)EndValue, StartTime, EndTime, Easing);
}
}
public override void Apply(Drawable d)
{
base.Apply(d);
(d as PercentageCounter).DisplayedCount = CurrentValue;
((PercentageCounter)d).DisplayedCount = CurrentValue;
}
}
}

View File

@ -1,24 +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 osu.Framework.Configuration;
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 System;
using System.Collections.Generic;
using System.Diagnostics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
{
public abstract class RollingCounter<T> : Container
{
/// <summary>
/// The current value.
/// </summary>
public Bindable<T> Current = new Bindable<T>();
/// <summary>
/// Type of the Transform to use.
/// </summary>
/// <remarks>
/// Must be a subclass of Transform<T>
/// Must be a subclass of Transform(T)
/// </remarks>
protected virtual Type TransformType => typeof(Transform<T>);
@ -60,32 +66,6 @@ namespace osu.Game.Graphics.UserInterface
}
}
private T count;
/// <summary>
/// Actual value of counter.
/// </summary>
public virtual T Count
{
get
{
return count;
}
set
{
count = value;
if (IsLoaded)
{
TransformCount(displayedCount, count);
}
}
}
public void Set(T value)
{
Count = value;
}
public abstract void Increment(T amount);
private float textSize;
@ -107,7 +87,7 @@ namespace osu.Game.Graphics.UserInterface
{
Children = new Drawable[]
{
DisplayedCountSpriteText = new OsuSpriteText()
DisplayedCountSpriteText = new OsuSpriteText
{
Font = @"Venera"
},
@ -116,7 +96,15 @@ namespace osu.Game.Graphics.UserInterface
TextSize = 40;
AutoSizeAxes = Axes.Both;
DisplayedCount = Count;
DisplayedCount = Current;
Current.ValueChanged += currentChanged;
}
private void currentChanged(object sender, EventArgs e)
{
if (IsLoaded)
TransformCount(displayedCount, Current);
}
protected override void LoadComplete()
@ -125,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface
Flush(false, TransformType);
DisplayedCountSpriteText.Text = FormatCount(count);
DisplayedCountSpriteText.Text = FormatCount(Current);
DisplayedCountSpriteText.Anchor = Anchor;
DisplayedCountSpriteText.Origin = Origin;
}
@ -136,7 +124,7 @@ namespace osu.Game.Graphics.UserInterface
/// <param name="count">New count value.</param>
public virtual void SetCountWithoutRolling(T count)
{
Count = count;
Current.Value = count;
StopRolling();
}
@ -146,7 +134,7 @@ namespace osu.Game.Graphics.UserInterface
public virtual void StopRolling()
{
Flush(false, TransformType);
DisplayedCount = Count;
DisplayedCount = Current;
}
/// <summary>
@ -211,7 +199,7 @@ namespace osu.Game.Graphics.UserInterface
if (RollingDuration < 1)
{
DisplayedCount = Count;
DisplayedCount = Current;
return;
}

View File

@ -8,7 +8,7 @@ using System;
namespace osu.Game.Graphics.UserInterface
{
public class ScoreCounter : RollingCounter<ulong>
public class ScoreCounter : RollingCounter<double>
{
protected override Type TransformType => typeof(TransformScore);
@ -34,24 +34,24 @@ namespace osu.Game.Graphics.UserInterface
LeadingZeroes = leading;
}
protected override double GetProportionalDuration(ulong currentValue, ulong newValue)
protected override double GetProportionalDuration(double currentValue, double newValue)
{
return currentValue > newValue ? currentValue - newValue : newValue - currentValue;
}
protected override string FormatCount(ulong count)
protected override string FormatCount(double count)
{
return count.ToString("D" + LeadingZeroes);
return ((long)count).ToString("D" + LeadingZeroes);
}
public override void Increment(ulong amount)
public override void Increment(double amount)
{
Count = Count + amount;
Current.Value = Current + amount;
}
protected class TransformScore : Transform<ulong>
protected class TransformScore : Transform<double>
{
protected override ulong CurrentValue
protected override double CurrentValue
{
get
{
@ -59,14 +59,14 @@ namespace osu.Game.Graphics.UserInterface
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return (ulong)Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
return Interpolation.ValueAt(time, (float)StartValue, (float)EndValue, StartTime, EndTime, Easing);
}
}
public override void Apply(Drawable d)
{
base.Apply(d);
(d as ScoreCounter).DisplayedCount = CurrentValue;
((ScoreCounter)d).DisplayedCount = CurrentValue;
}
}
}

View File

@ -32,7 +32,7 @@ namespace osu.Game.Graphics.UserInterface
private float minStarAlpha => 0.5f;
private const float star_size = 20;
private float star_spacing = 4;
private const float star_spacing = 4;
private float count;
@ -123,7 +123,7 @@ namespace osu.Game.Graphics.UserInterface
if (value <= i)
return minStarScale;
return i + 1 <= value ? 1.0f : Interpolation.ValueAt(value, minStarScale, 1.0f, i, i + 1);
return i + 1 <= value ? 1.0f : (float)Interpolation.ValueAt(value, minStarScale, 1.0f, i, i + 1);
}
private void transformCount(float newValue)
@ -145,7 +145,7 @@ namespace osu.Game.Graphics.UserInterface
}
}
class Star : Container
private class Star : Container
{
public TextAwesome Icon;
public Star()

View File

@ -4,13 +4,13 @@
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Graphics.UserInterface
{
@ -98,6 +98,7 @@ namespace osu.Game.Graphics.UserInterface
icon = new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 25,
},
}

View File

@ -87,7 +87,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
}
ScheduledDelegate popOutDelegate;
private ScheduledDelegate popOutDelegate;
private VolumeMeter volumeMeterEffect;
private VolumeMeter volumeMeterMusic;

View File

@ -8,7 +8,7 @@ using OpenTK.Input;
namespace osu.Game.Graphics.UserInterface.Volume
{
class VolumeControlReceptor : Container
internal class VolumeControlReceptor : Container
{
public Action<InputState> ActionRequested;

View File

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

View File

@ -6,23 +6,22 @@ using System.Collections.Generic;
using System.IO;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using osu.Game.Database;
namespace osu.Game.Beatmaps.IO
namespace osu.Game.IO
{
public abstract class ArchiveReader : IDisposable, IResourceStore<byte[]>
{
private class Reader
protected class Reader
{
public Func<Storage, string, bool> Test { get; set; }
public Type Type { get; set; }
}
private static List<Reader> readers { get; } = new List<Reader>();
protected static List<Reader> Readers { get; } = new List<Reader>();
public static ArchiveReader GetReader(Storage storage, string path)
{
foreach (var reader in readers)
foreach (var reader in Readers)
{
if (reader.Test(storage, path))
return (ArchiveReader)Activator.CreateInstance(reader.Type, storage.GetStream(path));
@ -32,24 +31,9 @@ namespace osu.Game.Beatmaps.IO
protected static void AddReader<T>(Func<Storage, string, bool> test) where T : ArchiveReader
{
readers.Add(new Reader { Test = test, Type = typeof(T) });
Readers.Add(new Reader { Test = test, Type = typeof(T) });
}
/// <summary>
/// Reads the beatmap metadata from this archive.
/// </summary>
public abstract BeatmapMetadata ReadMetadata();
/// <summary>
/// Gets a list of beatmap file names.
/// </summary>
public string[] BeatmapFilenames { get; protected set; }
/// <summary>
/// The storyboard filename. Null if no storyboard is present.
/// </summary>
public string StoryboardFilename { get; protected set; }
/// <summary>
/// Opens a stream for reading a specific file from this archive.
/// </summary>

View File

@ -0,0 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.IO.Legacy
{
public interface ILegacySerializable
{
void ReadFromStream(SerializationReader sr);
void WriteToStream(SerializationWriter sw);
}
}

View File

@ -0,0 +1,278 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
namespace osu.Game.IO.Legacy
{
/// <summary> SerializationReader. Extends BinaryReader to add additional data types,
/// handle null strings and simplify use with ISerializable. </summary>
public class SerializationReader : BinaryReader
{
private Stream stream;
public SerializationReader(Stream s)
: base(s, Encoding.UTF8)
{
stream = s;
}
public int RemainingBytes => (int)(stream.Length - stream.Position);
/// <summary> Static method to take a SerializationInfo object (an input to an ISerializable constructor)
/// and produce a SerializationReader from which serialized objects can be read </summary>.
public static SerializationReader GetReader(SerializationInfo info)
{
byte[] byteArray = (byte[])info.GetValue("X", typeof(byte[]));
MemoryStream ms = new MemoryStream(byteArray);
return new SerializationReader(ms);
}
/// <summary> Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. </summary>
public override string ReadString()
{
if (0 == ReadByte()) return null;
return base.ReadString();
}
/// <summary> Reads a byte array from the buffer, handling nulls and the array length. </summary>
public byte[] ReadByteArray()
{
int len = ReadInt32();
if (len > 0) return ReadBytes(len);
if (len < 0) return null;
return new byte[0];
}
/// <summary> Reads a char array from the buffer, handling nulls and the array length. </summary>
public char[] ReadCharArray()
{
int len = ReadInt32();
if (len > 0) return ReadChars(len);
if (len < 0) return null;
return new char[0];
}
/// <summary> Reads a DateTime from the buffer. </summary>
public DateTime ReadDateTime()
{
long ticks = ReadInt64();
if (ticks < 0) throw new AbandonedMutexException("oops");
return new DateTime(ticks, DateTimeKind.Utc);
}
/// <summary> Reads a generic list from the buffer. </summary>
public IList<T> ReadBList<T>(bool skipErrors = false) where T : ILegacySerializable, new()
{
int count = ReadInt32();
if (count < 0) return null;
IList<T> d = new List<T>(count);
SerializationReader sr = new SerializationReader(BaseStream);
for (int i = 0; i < count; i++)
{
T obj = new T();
try
{
obj.ReadFromStream(sr);
}
catch (Exception)
{
if (skipErrors)
continue;
throw;
}
d.Add(obj);
}
return d;
}
/// <summary> Reads a generic list from the buffer. </summary>
public IList<T> ReadList<T>()
{
int count = ReadInt32();
if (count < 0) return null;
IList<T> d = new List<T>(count);
for (int i = 0; i < count; i++) d.Add((T)ReadObject());
return d;
}
/// <summary> Reads a generic Dictionary from the buffer. </summary>
public IDictionary<T, U> ReadDictionary<T, U>()
{
int count = ReadInt32();
if (count < 0) return null;
IDictionary<T, U> d = new Dictionary<T, U>();
for (int i = 0; i < count; i++) d[(T)ReadObject()] = (U)ReadObject();
return d;
}
/// <summary> Reads an object which was added to the buffer by WriteObject. </summary>
public object ReadObject()
{
ObjType t = (ObjType)ReadByte();
switch (t)
{
case ObjType.boolType:
return ReadBoolean();
case ObjType.byteType:
return ReadByte();
case ObjType.uint16Type:
return ReadUInt16();
case ObjType.uint32Type:
return ReadUInt32();
case ObjType.uint64Type:
return ReadUInt64();
case ObjType.sbyteType:
return ReadSByte();
case ObjType.int16Type:
return ReadInt16();
case ObjType.int32Type:
return ReadInt32();
case ObjType.int64Type:
return ReadInt64();
case ObjType.charType:
return ReadChar();
case ObjType.stringType:
return base.ReadString();
case ObjType.singleType:
return ReadSingle();
case ObjType.doubleType:
return ReadDouble();
case ObjType.decimalType:
return ReadDecimal();
case ObjType.dateTimeType:
return ReadDateTime();
case ObjType.byteArrayType:
return ReadByteArray();
case ObjType.charArrayType:
return ReadCharArray();
case ObjType.otherType:
return DynamicDeserializer.Deserialize(BaseStream);
default:
return null;
}
}
public class DynamicDeserializer
{
private static VersionConfigToNamespaceAssemblyObjectBinder versionBinder;
private static BinaryFormatter formatter;
private static void initialize()
{
versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder();
formatter = new BinaryFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
Binder = versionBinder
};
}
public static object Deserialize(Stream stream)
{
if (formatter == null)
initialize();
Debug.Assert(formatter != null, "formatter != null");
return formatter.Deserialize(stream);
}
#region Nested type: VersionConfigToNamespaceAssemblyObjectBinder
public sealed class VersionConfigToNamespaceAssemblyObjectBinder : SerializationBinder
{
private readonly Dictionary<string, Type> cache = new Dictionary<string, Type>();
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize;
if (cache.TryGetValue(assemblyName + typeName, out typeToDeserialize))
return typeToDeserialize;
List<Type> tmpTypes = new List<Type>();
Type genType = null;
if (typeName.Contains("System.Collections.Generic") && typeName.Contains("[["))
{
string[] splitTyps = typeName.Split('[');
foreach (string typ in splitTyps)
{
if (typ.Contains("Version"))
{
string asmTmp = typ.Substring(typ.IndexOf(',') + 1);
string asmName = asmTmp.Remove(asmTmp.IndexOf(']')).Trim();
string typName = typ.Remove(typ.IndexOf(','));
tmpTypes.Add(BindToType(asmName, typName));
}
else if (typ.Contains("Generic"))
{
genType = BindToType(assemblyName, typ);
}
}
if (genType != null && tmpTypes.Count > 0)
{
return genType.MakeGenericType(tmpTypes.ToArray());
}
}
string toAssemblyName = assemblyName.Split(',')[0];
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in assemblies)
{
if (a.FullName.Split(',')[0] == toAssemblyName)
{
typeToDeserialize = a.GetType(typeName);
break;
}
}
cache.Add(assemblyName + typeName, typeToDeserialize);
return typeToDeserialize;
}
}
#endregion
}
}
public enum ObjType : byte
{
nullType,
boolType,
byteType,
uint16Type,
uint32Type,
uint64Type,
sbyteType,
int16Type,
int32Type,
int64Type,
charType,
stringType,
singleType,
doubleType,
decimalType,
dateTimeType,
byteArrayType,
charArrayType,
otherType,
ILegacySerializableType
}
}

View File

@ -0,0 +1,262 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
// ReSharper disable ConditionIsAlwaysTrueOrFalse (we're allowing nulls to be passed to the writer where the underlying class doesn't).
// ReSharper disable HeuristicUnreachableCode
namespace osu.Game.IO.Legacy
{
/// <summary> SerializationWriter. Extends BinaryWriter to add additional data types,
/// handle null strings and simplify use with ISerializable. </summary>
public class SerializationWriter : BinaryWriter
{
public SerializationWriter(Stream s)
: base(s, Encoding.UTF8)
{
}
/// <summary> Static method to initialise the writer with a suitable MemoryStream. </summary>
public static SerializationWriter GetWriter()
{
MemoryStream ms = new MemoryStream(1024);
return new SerializationWriter(ms);
}
/// <summary> Writes a string to the buffer. Overrides the base implementation so it can cope with nulls </summary>
public override void Write(string str)
{
if (str == null)
{
Write((byte)ObjType.nullType);
}
else
{
Write((byte)ObjType.stringType);
base.Write(str);
}
}
/// <summary> Writes a byte array to the buffer. Overrides the base implementation to
/// send the length of the array which is needed when it is retrieved </summary>
public override void Write(byte[] b)
{
if (b == null)
{
Write(-1);
}
else
{
int len = b.Length;
Write(len);
if (len > 0) base.Write(b);
}
}
/// <summary> Writes a char array to the buffer. Overrides the base implementation to
/// sends the length of the array which is needed when it is read. </summary>
public override void Write(char[] c)
{
if (c == null)
{
Write(-1);
}
else
{
int len = c.Length;
Write(len);
if (len > 0) base.Write(c);
}
}
/// <summary>
/// Writes DateTime to the buffer.
/// </summary>
/// <param name="dt"></param>
public void Write(DateTime dt)
{
Write(dt.ToUniversalTime().Ticks);
}
/// <summary> Writes a generic ICollection (such as an IList(T)) to the buffer.</summary>
public void Write<T>(List<T> c) where T : ILegacySerializable
{
if (c == null)
{
Write(-1);
}
else
{
int count = c.Count;
Write(count);
for (int i = 0; i < count; i++)
c[i].WriteToStream(this);
}
}
/// <summary> Writes a generic IDictionary to the buffer. </summary>
public void Write<T, U>(IDictionary<T, U> d)
{
if (d == null)
{
Write(-1);
}
else
{
Write(d.Count);
foreach (KeyValuePair<T, U> kvp in d)
{
WriteObject(kvp.Key);
WriteObject(kvp.Value);
}
}
}
/// <summary> Writes an arbitrary object to the buffer. Useful where we have something of type "object"
/// and don't know how to treat it. This works out the best method to use to write to the buffer. </summary>
public void WriteObject(object obj)
{
if (obj == null)
{
Write((byte)ObjType.nullType);
}
else
{
switch (obj.GetType().Name)
{
case "Boolean":
Write((byte)ObjType.boolType);
Write((bool)obj);
break;
case "Byte":
Write((byte)ObjType.byteType);
Write((byte)obj);
break;
case "UInt16":
Write((byte)ObjType.uint16Type);
Write((ushort)obj);
break;
case "UInt32":
Write((byte)ObjType.uint32Type);
Write((uint)obj);
break;
case "UInt64":
Write((byte)ObjType.uint64Type);
Write((ulong)obj);
break;
case "SByte":
Write((byte)ObjType.sbyteType);
Write((sbyte)obj);
break;
case "Int16":
Write((byte)ObjType.int16Type);
Write((short)obj);
break;
case "Int32":
Write((byte)ObjType.int32Type);
Write((int)obj);
break;
case "Int64":
Write((byte)ObjType.int64Type);
Write((long)obj);
break;
case "Char":
Write((byte)ObjType.charType);
base.Write((char)obj);
break;
case "String":
Write((byte)ObjType.stringType);
base.Write((string)obj);
break;
case "Single":
Write((byte)ObjType.singleType);
Write((float)obj);
break;
case "Double":
Write((byte)ObjType.doubleType);
Write((double)obj);
break;
case "Decimal":
Write((byte)ObjType.decimalType);
Write((decimal)obj);
break;
case "DateTime":
Write((byte)ObjType.dateTimeType);
Write((DateTime)obj);
break;
case "Byte[]":
Write((byte)ObjType.byteArrayType);
base.Write((byte[])obj);
break;
case "Char[]":
Write((byte)ObjType.charArrayType);
base.Write((char[])obj);
break;
default:
Write((byte)ObjType.otherType);
BinaryFormatter b = new BinaryFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
TypeFormat = FormatterTypeStyle.TypesWhenNeeded
};
b.Serialize(BaseStream, obj);
break;
} // switch
} // if obj==null
} // WriteObject
/// <summary> Adds the SerializationWriter buffer to the SerializationInfo at the end of GetObjectData(). </summary>
public void AddToInfo(SerializationInfo info)
{
byte[] b = ((MemoryStream)BaseStream).ToArray();
info.AddValue("X", b, typeof(byte[]));
}
public void WriteRawBytes(byte[] b)
{
base.Write(b);
}
public void WriteByteArray(byte[] b)
{
if (b == null)
{
Write(-1);
}
else
{
int len = b.Length;
Write(len);
if (len > 0) base.Write(b);
}
}
public void WriteUtf8(string str)
{
WriteRawBytes(Encoding.UTF8.GetBytes(str));
}
}
}

View File

@ -0,0 +1,46 @@
// 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.Diagnostics;
using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Database;
namespace osu.Game.IPC
{
public class BeatmapIPCChannel : IpcChannel<BeatmapImportMessage>
{
private BeatmapDatabase beatmaps;
public BeatmapIPCChannel(IIpcHost host, BeatmapDatabase beatmaps = null)
: base(host)
{
this.beatmaps = beatmaps;
MessageReceived += msg =>
{
Debug.Assert(beatmaps != null);
ImportAsync(msg.Path).ContinueWith(t =>
{
if (t.Exception != null) throw t.Exception;
}, TaskContinuationOptions.OnlyOnFaulted);
};
}
public async Task ImportAsync(string path)
{
if (beatmaps == null)
{
//we want to contact a remote osu! to handle the import.
await SendMessageAsync(new BeatmapImportMessage { Path = path });
return;
}
beatmaps.Import(path);
}
}
public class BeatmapImportMessage
{
public string Path;
}
}

View File

@ -1,46 +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.Diagnostics;
using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Database;
namespace osu.Game.IPC
{
public class BeatmapImporter
{
private IpcChannel<BeatmapImportMessage> channel;
private BeatmapDatabase beatmaps;
public BeatmapImporter(GameHost host, BeatmapDatabase beatmaps = null)
{
this.beatmaps = beatmaps;
channel = new IpcChannel<BeatmapImportMessage>(host);
channel.MessageReceived += messageReceived;
}
public async Task ImportAsync(string path)
{
if (beatmaps != null)
beatmaps.Import(path);
else
{
await channel.SendMessageAsync(new BeatmapImportMessage { Path = path });
}
}
private void messageReceived(BeatmapImportMessage msg)
{
Debug.Assert(beatmaps != null);
ImportAsync(msg.Path);
}
}
public class BeatmapImportMessage
{
public string Path;
}
}

View File

@ -0,0 +1,46 @@
// 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.Diagnostics;
using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Game.Database;
namespace osu.Game.IPC
{
public class ScoreIPCChannel : IpcChannel<ScoreImportMessage>
{
private ScoreDatabase scores;
public ScoreIPCChannel(IIpcHost host, ScoreDatabase scores = null)
: base(host)
{
this.scores = scores;
MessageReceived += msg =>
{
Debug.Assert(scores != null);
ImportAsync(msg.Path).ContinueWith(t =>
{
if (t.Exception != null) throw t.Exception;
}, TaskContinuationOptions.OnlyOnFaulted);
};
}
public async Task ImportAsync(string path)
{
if (scores == null)
{
//we want to contact a remote osu! to handle the import.
await SendMessageAsync(new ScoreImportMessage { Path = path });
return;
}
scores.ReadReplayFile(path);
}
}
public class ScoreImportMessage
{
public string Path;
}
}

View File

@ -0,0 +1,33 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Input.Handlers;
using osu.Framework.Platform;
using OpenTK;
namespace osu.Game.Input.Handlers
{
public abstract class ReplayInputHandler : InputHandler
{
/// <summary>
/// A function provided to convert replay coordinates from gamefield to screen space.
/// </summary>
public Func<Vector2, Vector2> ToScreenSpace { protected get; set; }
/// <summary>
/// Update the current frame based on an incoming time value.
/// There are cases where we return a "must-use" time value that is different from the input.
/// This is to ensure accurate playback of replay data.
/// </summary>
/// <param name="time">The time which we should use for finding the current frame.</param>
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
public abstract double? SetFrameFromTime(double time);
public override bool Initialize(GameHost host) => true;
public override bool IsActive => true;
public override int Priority => 0;
}
}

View File

@ -0,0 +1,274 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.IO;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Input.Handlers;
using osu.Game.IO.Legacy;
using OpenTK;
using OpenTK.Input;
using KeyboardState = osu.Framework.Input.KeyboardState;
using MouseState = osu.Framework.Input.MouseState;
namespace osu.Game.Modes
{
public class LegacyReplay : Replay
{
protected List<LegacyReplayFrame> Frames = new List<LegacyReplayFrame>();
protected LegacyReplay()
{
}
public LegacyReplay(StreamReader reader)
{
float lastTime = 0;
foreach (var l in reader.ReadToEnd().Split(','))
{
var split = l.Split('|');
if (split.Length < 4 || float.Parse(split[0]) < 0) continue;
lastTime += float.Parse(split[0]);
Frames.Add(new LegacyReplayFrame(
lastTime,
float.Parse(split[1]),
384 - float.Parse(split[2]),
(LegacyButtonState)int.Parse(split[3])
));
}
}
public override ReplayInputHandler GetInputHandler() => new LegacyReplayInputHandler(Frames);
/// <summary>
/// The ReplayHandler will take a replay and handle the propagation of updates to the input stack.
/// It handles logic of any frames which *must* be executed.
/// </summary>
public class LegacyReplayInputHandler : ReplayInputHandler
{
private readonly List<LegacyReplayFrame> replayContent;
public LegacyReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex];
public LegacyReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex];
private int currentFrameIndex;
private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1);
public LegacyReplayInputHandler(List<LegacyReplayFrame> replayContent)
{
this.replayContent = replayContent;
}
private bool advanceFrame()
{
int newFrame = nextFrameIndex;
//ensure we aren't at an extent.
if (newFrame == currentFrameIndex) return false;
currentFrameIndex = newFrame;
return true;
}
public void SetPosition(Vector2 pos)
{
}
private Vector2? position
{
get
{
if (!hasFrames)
return null;
return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
}
}
public override List<InputState> GetPendingStates()
{
return new List<InputState>
{
new InputState
{
Mouse = new ReplayMouseState(
ToScreenSpace(position ?? Vector2.Zero),
new List<MouseState.ButtonState>
{
new MouseState.ButtonState(MouseButton.Left)
{
State = CurrentFrame?.MouseLeft ?? false
},
new MouseState.ButtonState(MouseButton.Right)
{
State = CurrentFrame?.MouseRight ?? false
},
}
),
Keyboard = new ReplayKeyboardState(new List<Key>())
}
};
}
public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1;
public bool AtFirstFrame => currentFrameIndex == 0;
public Vector2 Size => new Vector2(512, 384);
private const double sixty_frame_time = 1000.0 / 60;
private double currentTime;
private int currentDirection;
/// <summary>
/// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data.
/// Disabling this can make replay playback smoother (useful for autoplay, currently).
/// </summary>
public bool FrameAccuratePlayback = true;
private bool hasFrames => replayContent.Count > 0;
private bool inImportantSection =>
FrameAccuratePlayback &&
//a button is in a pressed state
(currentDirection > 0 ? CurrentFrame : NextFrame)?.ButtonState > LegacyButtonState.None &&
//the next frame is within an allowable time span
Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2;
/// <summary>
/// Update the current frame based on an incoming time value.
/// There are cases where we return a "must-use" time value that is different from the input.
/// This is to ensure accurate playback of replay data.
/// </summary>
/// <param name="time">The time which we should use for finding the current frame.</param>
/// <returns>The usable time value. If null, we should not advance time as we do not have enough data.</returns>
public override double? SetFrameFromTime(double time)
{
currentDirection = time.CompareTo(currentTime);
if (currentDirection == 0) currentDirection = 1;
if (hasFrames)
{
//if we changed frames, we want to execute once *exactly* on the frame's time.
if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame())
return currentTime = CurrentFrame.Time;
//if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null.
if (inImportantSection)
return null;
}
return currentTime = time;
}
private class ReplayMouseState : MouseState
{
public ReplayMouseState(Vector2 position, List<ButtonState> list)
{
Position = position;
ButtonStates = list;
}
}
private class ReplayKeyboardState : KeyboardState
{
public ReplayKeyboardState(List<Key> keys)
{
Keys = keys;
}
}
}
[Flags]
public enum LegacyButtonState
{
None = 0,
Left1 = 1,
Right1 = 2,
Left2 = 4,
Right2 = 8,
Smoke = 16
}
public class LegacyReplayFrame
{
public Vector2 Position => new Vector2(MouseX, MouseY);
public float MouseX;
public float MouseY;
public bool MouseLeft;
public bool MouseRight;
public bool MouseLeft1;
public bool MouseRight1;
public bool MouseLeft2;
public bool MouseRight2;
public LegacyButtonState ButtonState;
public double Time;
public LegacyReplayFrame(double time, float posX, float posY, LegacyButtonState buttonState)
{
MouseX = posX;
MouseY = posY;
ButtonState = buttonState;
SetButtonStates(buttonState);
Time = time;
}
public void SetButtonStates(LegacyButtonState buttonState)
{
ButtonState = buttonState;
MouseLeft = (buttonState & (LegacyButtonState.Left1 | LegacyButtonState.Left2)) > 0;
MouseLeft1 = (buttonState & LegacyButtonState.Left1) > 0;
MouseLeft2 = (buttonState & LegacyButtonState.Left2) > 0;
MouseRight = (buttonState & (LegacyButtonState.Right1 | LegacyButtonState.Right2)) > 0;
MouseRight1 = (buttonState & LegacyButtonState.Right1) > 0;
MouseRight2 = (buttonState & LegacyButtonState.Right2) > 0;
}
public LegacyReplayFrame(Stream s) : this(new SerializationReader(s))
{
}
public LegacyReplayFrame(SerializationReader sr)
{
ButtonState = (LegacyButtonState)sr.ReadByte();
SetButtonStates(ButtonState);
byte bt = sr.ReadByte();
if (bt > 0)//Handle Pre-Taiko compatible replays.
SetButtonStates(LegacyButtonState.Right1);
MouseX = sr.ReadSingle();
MouseY = sr.ReadSingle();
Time = sr.ReadInt32();
}
public void ReadFromStream(SerializationReader sr)
{
throw new NotImplementedException();
}
public void WriteToStream(SerializationWriter sw)
{
sw.Write((byte)ButtonState);
sw.Write((byte)0);
sw.Write(MouseX);
sw.Write(MouseY);
sw.Write(Time);
}
public override string ToString()
{
return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}";
}
}
}
}

View File

@ -2,8 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.ComponentModel;
using osu.Game.Graphics;
using osu.Game.Screens.Play;
namespace osu.Game.Modes
{
@ -15,17 +15,17 @@ namespace osu.Game.Modes
/// <summary>
/// The name of this mod.
/// </summary>
public abstract Mods Name { get; }
public abstract string Name { get; }
/// <summary>
/// The icon of this mod.
/// </summary>
public abstract FontAwesome Icon { get; }
public virtual FontAwesome Icon => FontAwesome.fa_question;
/// <summary>
/// The user readable description of this mod.
/// </summary>
public abstract string Description { get; }
public virtual string Description => string.Empty;
/// <summary>
/// The score multiplier of this mod.
@ -35,234 +35,142 @@ namespace osu.Game.Modes
/// <summary>
/// Returns if this mod is ranked.
/// </summary>
public abstract bool Ranked { get; }
public virtual bool Ranked => false;
/// <summary>
/// The mods this mod cannot be enabled with.
/// </summary>
public abstract Mods[] DisablesMods { get; }
public virtual Type[] IncompatibleMods => new Type[] { };
/// <summary>
/// Direct access to the Player before load has run.
/// </summary>
/// <param name="player"></param>
public virtual void PlayerLoading(Player player) { }
}
public class MultiMod : Mod
{
public override Mods Name => Modes.Mods.None;
public override FontAwesome Icon => FontAwesome.fa_close;
public override string Description => @"";
public override string Name => string.Empty;
public override string Description => string.Empty;
public override double ScoreMultiplier => 0.0;
public override bool Ranked => false;
public override Mods[] DisablesMods => new Mods[] { };
public Mod[] Mods;
}
public abstract class ModNoFail : Mod
{
public override Mods Name => Mods.NoFail;
public override string Name => "NoFail";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nofail;
public override string Description => @"You can't fail, no matter what.";
public override string Description => "You can't fail, no matter what.";
public override double ScoreMultiplier => 0.5;
public override bool Ranked => true;
public override Mods[] DisablesMods => new Mods[] { Mods.Relax, Mods.Autopilot, Mods.SuddenDeath, Mods.Perfect };
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) };
}
public abstract class ModEasy : Mod
{
public override Mods Name => Mods.Easy;
public override string Name => "Easy";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_easy;
public override string Description => @"Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required.";
public override string Description => "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required.";
public override double ScoreMultiplier => 0.5;
public override bool Ranked => true;
public override Mods[] DisablesMods => new Mods[] { Mods.HardRock };
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
}
public abstract class ModHidden : Mod
{
public override Mods Name => Mods.Hidden;
public override string Name => "Hidden";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override bool Ranked => true;
}
public abstract class ModHardRock : Mod
{
public override Mods Name => Mods.HardRock;
public override string Name => "Hard Rock";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hardrock;
public override string Description => @"Everything just got a bit harder...";
public override Mods[] DisablesMods => new Mods[] { Mods.Easy };
public override string Description => "Everything just got a bit harder...";
public override Type[] IncompatibleMods => new[] { typeof(ModEasy) };
}
public abstract class ModSuddenDeath : Mod
{
public override Mods Name => Mods.SuddenDeath;
public override string Name => "Sudden Death";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_suddendeath;
public override string Description => @"Miss a note and fail.";
public override string Description => "Miss a note and fail.";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Mods[] DisablesMods => new Mods[] { Mods.NoFail, Mods.Relax, Mods.Autopilot, Mods.Autoplay, Mods.Cinema };
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
}
public abstract class ModDoubleTime : Mod
{
public override Mods Name => Mods.DoubleTime;
public override string Name => "Double Time";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_doubletime;
public override string Description => @"Zoooooooooom";
public override string Description => "Zoooooooooom";
public override bool Ranked => true;
public override Mods[] DisablesMods => new Mods[] { Mods.HalfTime };
public override Type[] IncompatibleMods => new[] { typeof(ModHalfTime) };
}
public abstract class ModRelax : Mod
{
public override Mods Name => Mods.Relax;
public override string Name => "Relax";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_relax;
public override double ScoreMultiplier => 0;
public override bool Ranked => false;
public override Mods[] DisablesMods => new Mods[] { Mods.Autopilot, Mods.Autoplay, Mods.Cinema, Mods.NoFail, Mods.SuddenDeath, Mods.Perfect };
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
}
public abstract class ModHalfTime : Mod
{
public override Mods Name => Mods.HalfTime;
public override string Name => "Half Time";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_halftime;
public override string Description => @"Less zoom";
public override string Description => "Less zoom";
public override bool Ranked => true;
public override Mods[] DisablesMods => new Mods[] { Mods.DoubleTime, Mods.Nightcore };
public override Type[] IncompatibleMods => new[] { typeof(ModDoubleTime) };
}
public abstract class ModNightcore : ModDoubleTime
{
public override Mods Name => Mods.Nightcore;
public override string Name => "Nightcore";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_nightcore;
public override string Description => @"uguuuuuuuu";
public override string Description => "uguuuuuuuu";
}
public abstract class ModFlashlight : Mod
{
public override Mods Name => Mods.Flashlight;
public override string Name => "Flashlight";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight;
public override string Description => @"Restricted view area.";
public override string Description => "Restricted view area.";
public override bool Ranked => true;
}
public class ModAutoplay : Mod
{
public override Mods Name => Mods.Autoplay;
public override string Name => "Autoplay";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
public override string Description => @"Watch a perfect automated play through the song";
public override string Description => "Watch a perfect automated play through the song";
public override double ScoreMultiplier => 0;
public override bool Ranked => false;
public override Mods[] DisablesMods => new Mods[] { Mods.Relax, Mods.Autopilot, Mods.SpunOut, Mods.SuddenDeath, Mods.Perfect };
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
public override void PlayerLoading(Player player)
{
base.PlayerLoading(player);
player.ReplayInputHandler = Ruleset.GetRuleset(player.Beatmap.PlayMode).CreateAutoplayScore(player.Beatmap.Beatmap)?.Replay?.GetInputHandler();
}
}
public abstract class ModPerfect : ModSuddenDeath
{
public override Mods Name => Mods.Perfect;
public override FontAwesome Icon => FontAwesome.fa_close;
public override string Description => @"SS or quit.";
public override string Name => "Perfect";
public override string Description => "SS or quit.";
}
public class ModCinema : ModAutoplay
{
public override Mods Name => Mods.Cinema;
public override string Name => "Cinema";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
}
[Flags]
public enum Mods
{
None = 0,
[Description(@"No Fail")]
NoFail = 1 << 0,
[Description(@"Easy")]
Easy = 1 << 1,
//NoVideo = 1 << 2,
[Description(@"Hidden")]
Hidden = 1 << 3,
[Description(@"Hard Rock")]
HardRock = 1 << 4,
[Description(@"Sudden Death")]
SuddenDeath = 1 << 5,
[Description(@"Double Time")]
DoubleTime = 1 << 6,
[Description(@"Relax")]
Relax = 1 << 7,
[Description(@"Halftime")]
HalfTime = 1 << 8,
[Description(@"Nightcore")]
Nightcore = 1 << 9,
[Description(@"Flashlight")]
Flashlight = 1 << 10,
[Description(@"Auto")]
Autoplay = 1 << 11,
[Description(@"Spun Out")]
SpunOut = 1 << 12,
[Description(@"Autopilot")]
Autopilot = 1 << 13,
[Description(@"Perfect")]
Perfect = 1 << 14,
[Description(@"4K")]
Key4 = 1 << 15,
[Description(@"5K")]
Key5 = 1 << 16,
[Description(@"6K")]
Key6 = 1 << 17,
[Description(@"7K")]
Key7 = 1 << 18,
[Description(@"8K")]
Key8 = 1 << 19,
[Description(@"Fade In")]
FadeIn = 1 << 20,
[Description(@"Random")]
Random = 1 << 21,
[Description(@"Cinema")]
Cinema = 1 << 22,
[Description(@"Target Practice")]
Target = 1 << 23,
[Description(@"9K")]
Key9 = 1 << 24,
[Description(@"Co-Op")]
KeyCoop = 1 << 25,
[Description(@"1K")]
Key1 = 1 << 26,
[Description(@"3K")]
Key3 = 1 << 27,
[Description(@"2K")]
Key2 = 1 << 28,
LastMod = 1 << 29,
KeyMod = Key1 | Key2 | Key3 | Key4 | Key5 | Key6 | Key7 | Key8 | Key9 | KeyCoop,
FreeModAllowed = NoFail | Easy | Hidden | HardRock | SuddenDeath | Flashlight | FadeIn | Relax | Autopilot | SpunOut | KeyMod,
ScoreIncreaseMods = Hidden | HardRock | DoubleTime | Flashlight | FadeIn
}
public enum ModType
{
DifficultyReduction,

View File

@ -16,22 +16,15 @@ namespace osu.Game.Modes.Objects.Drawables
{
public abstract class DrawableHitObject : Container, IStateful<ArmedState>
{
public event Action<DrawableHitObject, JudgementInfo> OnJudgement;
public override bool HandleInput => Interactive;
public bool Interactive = true;
public JudgementInfo Judgement;
public abstract JudgementInfo CreateJudgementInfo();
protected abstract JudgementInfo CreateJudgementInfo();
public HitObject HitObject;
public DrawableHitObject(HitObject hitObject)
{
HitObject = hitObject;
}
protected abstract void UpdateState(ArmedState state);
private ArmedState state;
public ArmedState State
@ -52,20 +45,11 @@ namespace osu.Game.Modes.Objects.Drawables
}
}
SampleChannel sample;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
string hitType = ((HitObject.Sample?.Type ?? SampleType.None) == SampleType.None ? SampleType.Normal : HitObject.Sample.Type).ToString().ToLower();
string sampleSet = (HitObject.Sample?.Set ?? SampleSet.Normal).ToString().ToLower();
sample = audio.Sample.Get($@"Gameplay/{sampleSet}-hit{hitType}");
}
protected SampleChannel Sample;
protected virtual void PlaySample()
{
sample?.Play();
Sample?.Play();
}
protected override void LoadComplete()
@ -81,24 +65,23 @@ namespace osu.Game.Modes.Objects.Drawables
Expire(true);
}
}
private List<DrawableHitObject> nestedHitObjects;
public abstract class DrawableHitObject<HitObjectType> : DrawableHitObject
where HitObjectType : HitObject
{
public event Action<DrawableHitObject<HitObjectType>, JudgementInfo> OnJudgement;
protected IEnumerable<DrawableHitObject> NestedHitObjects => nestedHitObjects;
public HitObjectType HitObject;
protected void AddNested(DrawableHitObject h)
protected DrawableHitObject(HitObjectType hitObject)
{
if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject>();
h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ;
nestedHitObjects.Add(h);
HitObject = hitObject;
}
/// <summary>
/// Process a hit of this hitobject. Carries out judgement.
/// </summary>
/// <param name="judgement">Preliminary judgement information provided by the hit source.</param>
/// <returns>Whether a hit was processed.</returns>
protected bool UpdateJudgement(bool userTriggered)
{
@ -143,7 +126,30 @@ namespace osu.Game.Modes.Objects.Drawables
UpdateJudgement(false);
}
protected abstract void UpdateState(ArmedState state);
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
SampleType type = HitObject.Sample?.Type ?? SampleType.None;
if (type == SampleType.None)
type = SampleType.Normal;
SampleSet sampleSet = HitObject.Sample?.Set ?? SampleSet.Normal;
Sample = audio.Sample.Get($@"Gameplay/{sampleSet.ToString().ToLower()}-hit{type.ToString().ToLower()}");
}
private List<DrawableHitObject<HitObjectType>> nestedHitObjects;
protected IEnumerable<DrawableHitObject<HitObjectType>> NestedHitObjects => nestedHitObjects;
protected void AddNested(DrawableHitObject<HitObjectType> h)
{
if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject<HitObjectType>>();
h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ;
nestedHitObjects.Add(h);
}
}
public enum ArmedState

View File

@ -1,19 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Modes
{
public enum PlayMode
{
[Description(@"osu!")]
Osu = 0,
[Description(@"osu!taiko")]
Taiko = 1,
[Description(@"osu!catch")]
Catch = 2,
[Description(@"osu!mania")]
Mania = 3
}
}

12
osu.Game/Modes/Replay.cs Normal file
View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Input.Handlers;
namespace osu.Game.Modes
{
public abstract class Replay
{
public virtual ReplayInputHandler GetInputHandler() => null;
}
}

View File

@ -1,14 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI;
using System;
using System.Collections.Concurrent;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays.Mods;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace osu.Game.Modes
{
@ -23,13 +23,13 @@ namespace osu.Game.Modes
{
private static ConcurrentDictionary<PlayMode, Type> availableRulesets = new ConcurrentDictionary<PlayMode, Type>();
public abstract ScoreOverlay CreateScoreOverlay();
public static IEnumerable<PlayMode> PlayModes => availableRulesets.Keys;
public virtual IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { };
public abstract IEnumerable<Mod> GetModsFor(ModType type);
public abstract ScoreProcessor CreateScoreProcessor(int hitObjectCount);
public abstract ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0);
public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap);
@ -43,6 +43,12 @@ namespace osu.Game.Modes
public virtual FontAwesome Icon => FontAwesome.fa_question_circle;
public abstract string Description { get; }
public abstract IEnumerable<KeyCounter> CreateGameplayKeys();
public virtual Score CreateAutoplayScore(Beatmap beatmap) => null;
public static Ruleset GetRuleset(PlayMode mode)
{
Type type;
@ -52,5 +58,6 @@ namespace osu.Game.Modes
return Activator.CreateInstance(type) as Ruleset;
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Users;
using osu.Game.Database;
namespace osu.Game.Modes
{
@ -9,10 +10,13 @@ namespace osu.Game.Modes
{
public double TotalScore { get; set; }
public double Accuracy { get; set; }
public double Combo { get; set; }
public double MaxCombo { get; set; }
public double Health { get; set; }
public Mod[] Mods { get; set; }
public User User { get; set; }
public int MaxCombo { get; set; }
public int Combo { get; set; }
public Replay Replay;
public BeatmapInfo Beatmap;
}
}

View File

@ -1,16 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Configuration;
using osu.Game.Modes.Objects.Drawables;
using System;
using System.Collections.Generic;
namespace osu.Game.Modes
{
public abstract class ScoreProcessor
{
public virtual Score GetScore() => new Score()
public virtual Score GetScore() => new Score
{
TotalScore = TotalScore,
Combo = Combo,
@ -51,7 +51,7 @@ namespace osu.Game.Modes
/// Initializes a new instance of the <see cref="ScoreProcessor"/> class.
/// </summary>
/// <param name="hitObjectCount">Number of HitObjects. It is used for specifying Judgements collection Capacity</param>
public ScoreProcessor(int hitObjectCount = 0)
protected ScoreProcessor(int hitObjectCount = 0)
{
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
Judgements = new List<JudgementInfo>(hitObjectCount);

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
@ -13,10 +14,12 @@ namespace osu.Game.Modes.UI
{
public abstract class ComboCounter : Container
{
public bool IsRolling
public BindableInt Current = new BindableInt
{
get; protected set;
}
MinValue = 0,
};
public bool IsRolling { get; protected set; }
protected SpriteText PopOutCount;
@ -37,60 +40,9 @@ namespace osu.Game.Modes.UI
/// </summary>
protected EasingTypes RollingEasing => EasingTypes.None;
private ulong displayedCount;
/// <summary>
/// Value shown at the current moment.
/// </summary>
public virtual ulong DisplayedCount
{
get
{
return displayedCount;
}
protected set
{
if (displayedCount.Equals(value))
return;
updateDisplayedCount(displayedCount, value, IsRolling);
}
}
private ulong count;
/// <summary>
/// Actual value of counter.
/// </summary>
public virtual ulong Count
{
get
{
return count;
}
set
{
updateCount(value);
}
}
public void Increment(ulong amount = 1)
{
Count = Count + amount;
}
protected SpriteText DisplayedCountSpriteText;
private float textSize;
public float TextSize
{
get { return textSize; }
set
{
textSize = value;
DisplayedCountSpriteText.TextSize = TextSize;
PopOutCount.TextSize = TextSize;
}
}
private int previousValue;
/// <summary>
/// Base of all combo counters.
@ -113,81 +65,97 @@ namespace osu.Game.Modes.UI
};
TextSize = 80;
Current.ValueChanged += comboChanged;
}
private void comboChanged(object sender, System.EventArgs e)
{
updateCount(Current.Value == 0);
}
protected override void LoadComplete()
{
base.LoadComplete();
DisplayedCountSpriteText.Text = FormatCount(Count);
DisplayedCountSpriteText.Text = FormatCount(Current);
DisplayedCountSpriteText.Anchor = Anchor;
DisplayedCountSpriteText.Origin = Origin;
StopRolling();
}
private int displayedCount;
/// <summary>
/// Value shown at the current moment.
/// </summary>
public virtual int DisplayedCount
{
get { return displayedCount; }
protected set
{
if (displayedCount.Equals(value))
return;
updateDisplayedCount(displayedCount, value, IsRolling);
}
}
private float textSize;
public float TextSize
{
get { return textSize; }
set
{
textSize = value;
DisplayedCountSpriteText.TextSize = TextSize;
PopOutCount.TextSize = TextSize;
}
}
/// <summary>
/// Increments the combo by an amount.
/// </summary>
/// <param name="amount"></param>
public void Increment(int amount = 1)
{
Current.Value = Current + amount;
}
/// <summary>
/// Stops rollover animation, forcing the displayed count to be the actual count.
/// </summary>
public void StopRolling()
{
updateCount(Count);
updateCount(false);
}
/// <summary>
/// Animates roll-back to 0.
/// </summary>
public void Roll()
{
Roll(0);
}
/// <summary>
/// Animates roll-up/roll-back to an specific value.
/// </summary>
/// <param name="newValue">Target value.</param>
public virtual void Roll(ulong newValue)
{
updateCount(newValue, true);
}
/// <summary>
/// Resets count to default value.
/// </summary>
public virtual void ResetCount()
{
updateCount(0);
}
protected virtual string FormatCount(ulong count)
protected virtual string FormatCount(int count)
{
return count.ToString();
}
protected abstract void OnDisplayedCountRolling(ulong currentValue, ulong newValue);
protected abstract void OnDisplayedCountIncrement(ulong newValue);
protected abstract void OnDisplayedCountChange(ulong newValue);
protected virtual void OnCountRolling(ulong currentValue, ulong newValue)
protected virtual void OnCountRolling(int currentValue, int newValue)
{
transformRoll(new TransformComboRoll(), currentValue, newValue);
}
protected virtual void OnCountIncrement(ulong currentValue, ulong newValue) {
protected virtual void OnCountIncrement(int currentValue, int newValue)
{
DisplayedCount = newValue;
}
protected virtual void OnCountChange(ulong currentValue, ulong newValue) {
protected virtual void OnCountChange(int currentValue, int newValue)
{
DisplayedCount = newValue;
}
private double getProportionalDuration(ulong currentValue, ulong newValue)
private double getProportionalDuration(int currentValue, int newValue)
{
double difference = currentValue > newValue ? currentValue - newValue : newValue - currentValue;
return difference * RollingDuration;
}
private void updateDisplayedCount(ulong currentValue, ulong newValue, bool rolling)
private void updateDisplayedCount(int currentValue, int newValue, bool rolling)
{
displayedCount = newValue;
if (rolling)
@ -198,10 +166,10 @@ namespace osu.Game.Modes.UI
OnDisplayedCountChange(newValue);
}
private void updateCount(ulong value, bool rolling = false)
private void updateCount(bool rolling)
{
ulong prevCount = count;
count = value;
int prev = previousValue;
previousValue = Current;
if (!IsLoaded)
return;
@ -210,27 +178,27 @@ namespace osu.Game.Modes.UI
{
Flush(false, typeof(TransformComboRoll));
IsRolling = false;
DisplayedCount = prevCount;
DisplayedCount = prev;
if (prevCount + 1 == count)
OnCountIncrement(prevCount, count);
if (prev + 1 == Current)
OnCountIncrement(prev, Current);
else
OnCountChange(prevCount, count);
OnCountChange(prev, Current);
}
else
{
OnCountRolling(displayedCount, count);
OnCountRolling(displayedCount, Current);
IsRolling = true;
}
}
private void transformRoll(TransformComboRoll transform, ulong currentValue, ulong newValue)
private void transformRoll(TransformComboRoll transform, int currentValue, int newValue)
{
Flush(false, typeof(TransformComboRoll));
if (RollingDuration < 1)
{
DisplayedCount = Count;
DisplayedCount = Current;
return;
}
@ -243,9 +211,9 @@ namespace osu.Game.Modes.UI
Transforms.Add(transform);
}
protected class TransformComboRoll : Transform<ulong>
protected class TransformComboRoll : Transform<int>
{
protected override ulong CurrentValue
protected override int CurrentValue
{
get
{
@ -253,23 +221,19 @@ namespace osu.Game.Modes.UI
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return (ulong)Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
return (int)Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
}
}
public override void Apply(Drawable d)
{
base.Apply(d);
(d as ComboCounter).DisplayedCount = CurrentValue;
((ComboCounter)d).DisplayedCount = CurrentValue;
}
}
public void Set(ulong value)
{
if (value == 0)
Roll();
else
Count = value;
}
protected abstract void OnDisplayedCountRolling(int currentValue, int newValue);
protected abstract void OnDisplayedCountIncrement(int newValue);
protected abstract void OnDisplayedCountChange(int newValue);
}
}

View File

@ -1,11 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using osu.Game.Graphics.UserInterface;
using System;
namespace osu.Game.Modes.UI
{
@ -31,7 +31,7 @@ namespace osu.Game.Modes.UI
public override void Increment(ulong amount)
{
Count = Count + amount;
Current.Value = Current + amount;
}
protected class TransformComboResult : Transform<ulong>
@ -51,7 +51,7 @@ namespace osu.Game.Modes.UI
public override void Apply(Drawable d)
{
base.Apply(d);
(d as ComboResultCounter).DisplayedCount = CurrentValue;
((ComboResultCounter)d).DisplayedCount = CurrentValue;
}
}
}

View File

@ -1,72 +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 System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.UI
{
public class HealthDisplay : Container
public abstract class HealthDisplay : Container
{
private Box background;
private Container fill;
public BindableDouble Current = new BindableDouble()
public readonly BindableDouble Current = new BindableDouble
{
MinValue = 0,
MaxValue = 1
};
public HealthDisplay()
protected HealthDisplay()
{
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
fill = new Container
{
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0, 1),
Masking = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
}
}
},
};
Current.ValueChanged += current_ValueChanged;
Current.ValueChanged += (s, e) => SetHealth((float)Current);
}
[BackgroundDependencyLoader]
private void laod(OsuColour colours)
{
fill.Colour = colours.BlueLighter;
fill.EdgeEffect = new EdgeEffect
{
Colour = colours.BlueDarker.Opacity(0.6f),
Radius = 8,
Type= EdgeEffectType.Glow
};
}
private void current_ValueChanged(object sender, EventArgs e)
{
fill.ScaleTo(new Vector2((float)Current, 1), 200, EasingTypes.OutQuint);
}
protected abstract void SetHealth(float value);
}
}

View File

@ -1,93 +1,106 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
using System;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Modes.UI
{
public abstract class HitRenderer : Container
{
public event Action<JudgementInfo> OnJudgement;
public event Action OnAllJudged;
internal readonly PlayerInputManager InputManager = new PlayerInputManager();
/// <summary>
/// A function to convert coordinates from gamefield to screen space.
/// </summary>
public abstract Func<Vector2, Vector2> MapPlayfieldToScreenSpace { get; }
/// <summary>
/// Whether all the HitObjects have been judged.
/// </summary>
protected abstract bool AllObjectsJudged { get; }
protected void TriggerOnJudgement(JudgementInfo j)
{
OnJudgement?.Invoke(j);
if (AllObjectsJudged)
OnAllJudged?.Invoke();
}
protected Playfield Playfield;
public bool AllObjectsJudged => Playfield.HitObjects.Children.First()?.Judgement.Result != null; //reverse depth sort means First() instead of Last().
public IEnumerable<DrawableHitObject> DrawableObjects => Playfield.HitObjects.Children;
}
public abstract class HitRenderer<T> : HitRenderer
where T : HitObject
public abstract class HitRenderer<TObject> : HitRenderer
where TObject : HitObject
{
private List<T> objects;
public override Func<Vector2, Vector2> MapPlayfieldToScreenSpace => Playfield.ScaledContent.ToScreenSpace;
public IEnumerable<DrawableHitObject> DrawableObjects => Playfield.HitObjects.Children;
public Beatmap Beatmap
protected override Container<Drawable> Content => content;
protected override bool AllObjectsJudged => Playfield.HitObjects.Children.All(h => h.Judgement.Result.HasValue);
protected Playfield<TObject> Playfield;
protected Beatmap<TObject> Beatmap;
private Container content;
protected HitRenderer(Beatmap beatmap)
{
set
{
objects = Convert(value);
if (IsLoaded)
loadObjects();
}
}
Beatmap = CreateBeatmapConverter().Convert(beatmap);
protected abstract Playfield CreatePlayfield();
protected abstract HitObjectConverter<T> Converter { get; }
protected virtual List<T> Convert(Beatmap beatmap) => Converter.Convert(beatmap);
public HitRenderer()
{
RelativeSizeAxes = Axes.Both;
InputManager.Add(content = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new[]
{
Playfield = CreatePlayfield(),
}
});
AddInternal(InputManager);
}
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
Playfield = CreatePlayfield()
};
loadObjects();
}
private void loadObjects()
{
if (objects == null) return;
foreach (T h in objects)
foreach (TObject h in Beatmap.HitObjects)
{
var drawableObject = GetVisualRepresentation(h);
DrawableHitObject<TObject> drawableObject = GetVisualRepresentation(h);
if (drawableObject == null) continue;
if (drawableObject == null)
continue;
drawableObject.OnJudgement += onJudgement;
Playfield.Add(drawableObject);
}
Playfield.PostProcess();
}
private void onJudgement(DrawableHitObject o, JudgementInfo j) => TriggerOnJudgement(j);
private void onJudgement(DrawableHitObject<TObject> o, JudgementInfo j) => TriggerOnJudgement(j);
protected abstract DrawableHitObject GetVisualRepresentation(T h);
protected abstract DrawableHitObject<TObject> GetVisualRepresentation(TObject h);
protected abstract Playfield<TObject> CreatePlayfield();
protected abstract IBeatmapConverter<TObject> CreateBeatmapConverter();
}
}

View File

@ -1,28 +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 System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Modes.Objects;
using OpenTK;
using osu.Framework.Graphics.Primitives;
using osu.Game.Screens.Play;
using osu.Framework.Allocation;
using osu.Game.Configuration;
using osu.Framework.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play;
using System;
namespace osu.Game.Modes.UI
{
public abstract class ScoreOverlay : Container
public abstract class HudOverlay : Container
{
public KeyCounterCollection KeyCounter;
public ComboCounter ComboCounter;
public ScoreCounter ScoreCounter;
public PercentageCounter AccuracyCounter;
public HealthDisplay HealthDisplay;
public Score Score { get; set; }
public readonly KeyCounterCollection KeyCounter;
public readonly ComboCounter ComboCounter;
public readonly ScoreCounter ScoreCounter;
public readonly PercentageCounter AccuracyCounter;
public readonly HealthDisplay HealthDisplay;
private Bindable<bool> showKeyCounter;
@ -30,31 +26,14 @@ namespace osu.Game.Modes.UI
protected abstract ComboCounter CreateComboCounter();
protected abstract PercentageCounter CreateAccuracyCounter();
protected abstract ScoreCounter CreateScoreCounter();
protected virtual HealthDisplay CreateHealthDisplay() => new HealthDisplay
{
Size = new Vector2(1, 5),
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Top = 20 }
};
protected abstract HealthDisplay CreateHealthDisplay();
public virtual void OnHit(HitObject h)
{
ComboCounter?.Increment();
ScoreCounter?.Increment(300);
AccuracyCounter?.Set(Math.Min(1, AccuracyCounter.Count + 0.01f));
}
public virtual void OnMiss(HitObject h)
{
ComboCounter?.Roll();
AccuracyCounter?.Set(AccuracyCounter.Count - 0.01f);
}
public ScoreOverlay()
protected HudOverlay()
{
RelativeSizeAxes = Axes.Both;
Children = new Drawable[] {
Children = new Drawable[]
{
KeyCounter = CreateKeyCounter(),
ComboCounter = CreateComboCounter(),
ScoreCounter = CreateScoreCounter(),
@ -81,11 +60,17 @@ namespace osu.Game.Modes.UI
public void BindProcessor(ScoreProcessor processor)
{
//bind processor bindables to combocounter, score display etc.
processor.TotalScore.ValueChanged += delegate { ScoreCounter?.Set((ulong)processor.TotalScore.Value); };
processor.Accuracy.ValueChanged += delegate { AccuracyCounter?.Set((float)processor.Accuracy.Value); };
processor.Combo.ValueChanged += delegate { ComboCounter?.Set((ulong)processor.Combo.Value); };
//bind processor bindables to combocounter, score display etc.
//TODO: these should be bindable binds, not events!
ScoreCounter?.Current.BindTo(processor.TotalScore);
AccuracyCounter?.Current.BindTo(processor.Accuracy);
ComboCounter?.Current.BindTo(processor.Combo);
HealthDisplay?.Current.BindTo(processor.Health);
}
public void BindHitRenderer(HitRenderer hitRenderer)
{
hitRenderer.InputManager.Add(KeyCounter.GetReceptor());
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
@ -25,31 +25,27 @@ namespace osu.Game.Modes.UI
reapplySize();
}
}
private Color4 backgroundColour;
new public Color4 Colour
public new Color4 Colour
{
get
{
return backgroundColour;
return background.Colour;
}
set
{
backgroundColour = value;
background.Colour = value;
}
}
private FontAwesome icon;
public FontAwesome Icon
{
get
{
return icon;
return modIcon.Icon;
}
set
{
icon = value;
modIcon.Icon = value;
}
}

View File

@ -1,32 +1,49 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables;
using OpenTK;
namespace osu.Game.Modes.UI
{
public abstract class Playfield : Container
public abstract class Playfield<T> : Container
where T : HitObject
{
public HitObjectContainer HitObjects;
private Container<Drawable> content;
public HitObjectContainer<DrawableHitObject<T>> HitObjects;
public virtual void Add(DrawableHitObject h) => HitObjects.Add(h);
public virtual void Add(DrawableHitObject<T> h) => HitObjects.Add(h);
internal Container<Drawable> ScaledContent;
public override bool Contains(Vector2 screenSpacePos) => true;
protected override Container<Drawable> Content => content;
public Playfield()
private Container<Drawable> content;
/// <summary>
/// A container for keeping track of DrawableHitObjects.
/// </summary>
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width.</param>
protected Playfield(float? customWidth = null)
{
AddInternal(content = new ScaledContainer()
AddInternal(ScaledContent = new ScaledContainer
{
CustomWidth = customWidth,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
content = new Container
{
RelativeSizeAxes = Axes.Both,
}
}
});
Add(HitObjects = new HitObjectContainer
Add(HitObjects = new HitObjectContainer<DrawableHitObject<T>>
{
RelativeSizeAxes = Axes.Both,
});
@ -36,14 +53,20 @@ namespace osu.Game.Modes.UI
{
}
public class ScaledContainer : Container
private class ScaledContainer : Container
{
protected override Vector2 DrawScale => new Vector2(DrawSize.X / 512);
/// <summary>
/// A value (in game pixels that we should scale our content to match).
/// </summary>
public float? CustomWidth;
//dividing by the customwidth will effectively scale our content to the required container size.
protected override Vector2 DrawScale => CustomWidth.HasValue ? new Vector2(DrawSize.X / CustomWidth.Value) : base.DrawScale;
public override bool Contains(Vector2 screenSpacePos) => true;
}
public class HitObjectContainer : Container<DrawableHitObject>
public class HitObjectContainer<U> : Container<U> where U : Drawable
{
public override bool Contains(Vector2 screenSpacePos) => true;
}

View File

@ -0,0 +1,140 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
namespace osu.Game.Modes.UI
{
/// <summary>
/// Uses the 'x' symbol and has a pop-out effect while rolling over.
/// </summary>
public class StandardComboCounter : ComboCounter
{
protected uint ScheduledPopOutCurrentId;
protected virtual float PopOutSmallScale => 1.1f;
protected virtual bool CanPopOutWhileRolling => false;
public new Vector2 PopOutScale = new Vector2(1.6f);
protected override void LoadComplete()
{
base.LoadComplete();
PopOutCount.Origin = Origin;
PopOutCount.Anchor = Anchor;
}
protected override string FormatCount(int count)
{
return $@"{count}x";
}
protected virtual void TransformPopOut(int newValue)
{
PopOutCount.Text = FormatCount(newValue);
PopOutCount.ScaleTo(PopOutScale);
PopOutCount.FadeTo(PopOutInitialAlpha);
PopOutCount.MoveTo(Vector2.Zero);
PopOutCount.ScaleTo(1, PopOutDuration, PopOutEasing);
PopOutCount.FadeOut(PopOutDuration, PopOutEasing);
PopOutCount.MoveTo(DisplayedCountSpriteText.Position, PopOutDuration, PopOutEasing);
}
protected virtual void TransformPopOutRolling(int newValue)
{
TransformPopOut(newValue);
TransformPopOutSmall(newValue);
}
protected virtual void TransformNoPopOut(int newValue)
{
DisplayedCountSpriteText.Text = FormatCount(newValue);
DisplayedCountSpriteText.ScaleTo(1);
}
protected virtual void TransformPopOutSmall(int newValue)
{
DisplayedCountSpriteText.Text = FormatCount(newValue);
DisplayedCountSpriteText.ScaleTo(PopOutSmallScale);
DisplayedCountSpriteText.ScaleTo(1, PopOutDuration, PopOutEasing);
}
protected virtual void ScheduledPopOutSmall(uint id)
{
// Too late; scheduled task invalidated
if (id != ScheduledPopOutCurrentId)
return;
DisplayedCount++;
}
protected override void OnCountRolling(int currentValue, int newValue)
{
ScheduledPopOutCurrentId++;
// Hides displayed count if was increasing from 0 to 1 but didn't finish
if (currentValue == 0 && newValue == 0)
DisplayedCountSpriteText.FadeOut(FadeOutDuration);
base.OnCountRolling(currentValue, newValue);
}
protected override void OnCountIncrement(int currentValue, int newValue)
{
ScheduledPopOutCurrentId++;
if (DisplayedCount < currentValue)
DisplayedCount++;
DisplayedCountSpriteText.Show();
TransformPopOut(newValue);
uint newTaskId = ScheduledPopOutCurrentId;
Scheduler.AddDelayed(delegate
{
ScheduledPopOutSmall(newTaskId);
}, PopOutDuration);
}
protected override void OnCountChange(int currentValue, int newValue)
{
ScheduledPopOutCurrentId++;
if (newValue == 0)
DisplayedCountSpriteText.FadeOut();
base.OnCountChange(currentValue, newValue);
}
protected override void OnDisplayedCountRolling(int currentValue, int newValue)
{
if (newValue == 0)
DisplayedCountSpriteText.FadeOut(FadeOutDuration);
else
DisplayedCountSpriteText.Show();
if (CanPopOutWhileRolling)
TransformPopOutRolling(newValue);
else
TransformNoPopOut(newValue);
}
protected override void OnDisplayedCountChange(int newValue)
{
DisplayedCountSpriteText.FadeTo(newValue == 0 ? 0 : 1);
TransformNoPopOut(newValue);
}
protected override void OnDisplayedCountIncrement(int newValue)
{
DisplayedCountSpriteText.Show();
TransformPopOutSmall(newValue);
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics;
namespace osu.Game.Modes.UI
{
public class StandardHealthDisplay : HealthDisplay
{
private Container fill;
public StandardHealthDisplay()
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
fill = new Container
{
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0, 1),
Masking = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
}
}
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
fill.Colour = colours.BlueLighter;
fill.EdgeEffect = new EdgeEffect
{
Colour = colours.BlueDarker.Opacity(0.6f),
Radius = 8,
Type = EdgeEffectType.Glow
};
}
protected override void SetHealth(float value) => fill.ScaleTo(new Vector2(value, 1), 200, EasingTypes.OutQuint);
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play;
namespace osu.Game.Modes.UI
{
public class StandardHudOverlay : HudOverlay
{
protected override PercentageCounter CreateAccuracyCounter() => new PercentageCounter
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, 65),
TextSize = 20,
Margin = new MarginPadding { Right = 5 },
};
protected override ComboCounter CreateComboCounter() => new StandardComboCounter
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
};
protected override HealthDisplay CreateHealthDisplay() => new StandardHealthDisplay
{
Size = new Vector2(1, 5),
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Top = 20 }
};
protected override KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection
{
IsCounting = true,
FadeTime = 50,
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Margin = new MarginPadding(10),
};
protected override ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TextSize = 40,
Position = new Vector2(0, 30),
Margin = new MarginPadding { Right = 5 },
};
}
}

View File

@ -20,10 +20,10 @@ namespace osu.Game.Online.API
private OAuth authentication;
public string Endpoint = @"https://new.ppy.sh";
const string client_id = @"5";
const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
private const string client_id = @"5";
private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk";
ConcurrentQueue<APIRequest> queue = new ConcurrentQueue<APIRequest>();
private ConcurrentQueue<APIRequest> queue = new ConcurrentQueue<APIRequest>();
public Scheduler Scheduler = new Scheduler();
@ -38,22 +38,15 @@ namespace osu.Game.Online.API
public string Token
{
get { return authentication.Token?.ToString(); }
set
{
if (string.IsNullOrEmpty(value))
authentication.Token = null;
else
authentication.Token = OAuthToken.Parse(value);
}
set { authentication.Token = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); }
}
protected bool HasLogin => Token != null || (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password));
protected bool HasLogin => Token != null || !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password);
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (should dispose of this or at very least keep a reference).
private Thread thread;
Logger log;
private Logger log;
public APIAccess()
{
@ -88,22 +81,22 @@ namespace osu.Game.Online.API
/// <summary>
/// Number of consecutive requests which failed due to network issues.
/// </summary>
int failureCount = 0;
private int failureCount;
private void run()
{
while (true)
while (thread.IsAlive)
{
switch (State)
{
case APIState.Failing:
//todo: replace this with a ping request.
log.Add($@"In a failing state, waiting a bit before we try again...");
log.Add(@"In a failing state, waiting a bit before we try again...");
Thread.Sleep(5000);
if (queue.Count == 0)
{
log.Add($@"Queueing a ping request");
Queue(new ListChannelsRequest() { Timeout = 5000 });
log.Add(@"Queueing a ping request");
Queue(new ListChannelsRequest { Timeout = 5000 });
}
break;
case APIState.Offline:
@ -131,7 +124,7 @@ namespace osu.Game.Online.API
var userReq = new GetUserRequest();
userReq.Success += (u) => {
userReq.Success += u => {
LocalUser.Value = u;
//we're connected!
State = APIState.Online;
@ -291,7 +284,7 @@ namespace osu.Game.Online.API
if (failOldRequests)
{
APIRequest req;
while (queue.TryDequeue(out req))
while (oldQueue.TryDequeue(out req))
req.Fail(new Exception(@"Disconnected from server"));
}
}

View File

@ -22,7 +22,7 @@ namespace osu.Game.Online.API
private void onSuccess()
{
Success?.Invoke((WebRequest as JsonWebRequest<T>).ResponseObject);
Success?.Invoke(((JsonWebRequest<T>)WebRequest).ResponseObject);
}
public new event APISuccessHandler<T> Success;
@ -89,5 +89,5 @@ namespace osu.Game.Online.API
public delegate void APIFailureHandler(Exception e);
public delegate void APISuccessHandler();
public delegate void APISuccessHandler<T>(T content);
public delegate void APISuccessHandler<in T>(T content);
}

View File

@ -48,7 +48,7 @@ namespace osu.Game.Online.API
try
{
string[] parts = value.Split('/');
return new OAuthToken()
return new OAuthToken
{
AccessToken = parts[0],
AccessTokenExpiry = long.Parse(parts[1], NumberFormatInfo.InvariantInfo),

View File

@ -9,8 +9,8 @@ namespace osu.Game.Online.API.Requests
{
public class GetMessagesRequest : APIRequest<List<Message>>
{
List<Channel> channels;
long? since;
private List<Channel> channels;
private long? since;
public GetMessagesRequest(List<Channel> channels, long? sinceId)
{

View File

@ -1,55 +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.Diagnostics;
using System.Security;
using osu.Framework.Extensions;
namespace osu.Game.Online.API
{
internal class SecurePassword
{
private readonly SecureString storage = new SecureString();
private readonly Representation representation;
//todo: move this to a central constants file.
private const string password_entropy = @"cu24180ncjeiu0ci1nwui";
public SecurePassword(string input, bool encrypted = false)
{
//if (encrypted)
//{
// string rep;
// input = DPAPI.Decrypt(input, password_entropy, out rep);
// Enum.TryParse(rep, out representation);
//}
//else
{
representation = Representation.Raw;
}
foreach (char c in input)
storage.AppendChar(c);
storage.MakeReadOnly();
}
internal string Get(Representation request = Representation.Raw)
{
Debug.Assert(representation == request);
switch (request)
{
default:
return storage.UnsecureRepresentation();
//case Representation.Encrypted:
// return DPAPI.Encrypt(DPAPI.KeyType.UserKey, storage.UnsecureRepresentation(), password_entropy, representation.ToString());
}
}
}
enum Representation
{
Raw,
Encrypted
}
}

View File

@ -59,8 +59,8 @@ namespace osu.Game.Online.Chat.Drawables
return username_colours[message.UserId % username_colours.Length];
}
const float padding = 200;
const float text_size = 20;
private const float padding = 200;
private const float text_size = 20;
public ChatLine(Message message)
{

View File

@ -7,7 +7,6 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Threading;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Online.Chat.Drawables

View File

@ -13,7 +13,6 @@ using osu.Game.Input;
using OpenTK.Input;
using osu.Framework.Logging;
using osu.Game.Graphics.UserInterface.Volume;
using osu.Game.Database;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Timing;
@ -24,16 +23,16 @@ using osu.Game.Screens.Menu;
using OpenTK;
using System.Linq;
using osu.Framework.Graphics.Primitives;
using System.Collections.Generic;
using System.Threading.Tasks;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Overlays.Notifications;
using osu.Game.Screens.Play;
namespace osu.Game
{
public class OsuGame : OsuGameBase
{
public virtual bool IsDeployedBuild => false;
public Toolbar Toolbar;
private ChatOverlay chat;
@ -61,7 +60,7 @@ namespace osu.Game
public Bindable<PlayMode> PlayMode;
string[] args;
private string[] args;
private OptionsOverlay options;
@ -84,7 +83,7 @@ namespace osu.Game
if (args?.Length > 0)
{
var paths = args.Where(a => !a.StartsWith(@"-"));
ImportBeatmapsAsync(paths);
Task.Run(() => BeatmapDatabase.Import(paths));
}
Dependencies.Cache(this);
@ -92,9 +91,41 @@ namespace osu.Game
PlayMode = LocalConfig.GetBindable<PlayMode>(OsuConfig.PlayMode);
}
protected async void ImportBeatmapsAsync(IEnumerable<string> paths)
private ScheduledDelegate scoreLoad;
protected void LoadScore(Score s)
{
await Task.Run(() => BeatmapDatabase.Import(paths));
scoreLoad?.Cancel();
var menu = intro.ChildScreen;
if (menu == null)
{
scoreLoad = Schedule(() => LoadScore(s));
return;
}
if (!menu.IsCurrentScreen)
{
menu.MakeCurrent();
Delay(500);
scoreLoad = Schedule(() => LoadScore(s));
return;
}
if (s.Beatmap == null)
{
notificationManager.Post(new SimpleNotification
{
Text = @"Tried to load a score for a beatmap we don't have!",
Icon = FontAwesome.fa_life_saver,
});
return;
}
Beatmap.Value = BeatmapDatabase.GetWorkingBeatmap(s.Beatmap);
menu.Push(new PlayerLoader(new Player { ReplayInputHandler = s.Replay.GetInputHandler() }));
}
protected override void LoadComplete()
@ -129,7 +160,7 @@ namespace osu.Game
//overlay elements
(chat = new ChatOverlay { Depth = 0 }).LoadAsync(this, overlayContent.Add);
(options = new OptionsOverlay { Depth = -1 }).LoadAsync(this, overlayContent.Add);
(musicController = new MusicController()
(musicController = new MusicController
{
Depth = -2,
Position = new Vector2(0, Toolbar.HEIGHT),
@ -194,7 +225,7 @@ namespace osu.Game
private bool globalHotkeyPressed(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
if (args.Repeat || intro == null) return false;
switch (args.Key)
{
@ -203,9 +234,11 @@ namespace osu.Game
return true;
case Key.PageUp:
case Key.PageDown:
var rate = ((Clock as ThrottledFrameClock).Source as StopwatchClock).Rate * (args.Key == Key.PageUp ? 1.1f : 0.9f);
((Clock as ThrottledFrameClock).Source as StopwatchClock).Rate = rate;
Logger.Log($@"Adjusting game clock to {rate}", LoggingTarget.Debug);
var swClock = (Clock as ThrottledFrameClock)?.Source as StopwatchClock;
if (swClock == null) return false;
swClock.Rate *= args.Key == Key.PageUp ? 1.1f : 0.9f;
Logger.Log($@"Adjusting game clock to {swClock.Rate}", LoggingTarget.Debug);
return true;
}

View File

@ -1,6 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using System.Reflection;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@ -25,6 +28,8 @@ namespace osu.Game
protected BeatmapDatabase BeatmapDatabase;
protected ScoreDatabase ScoreDatabase;
protected override string MainResourceFile => @"osu.Game.Resources.dll";
public APIAccess API;
@ -33,16 +38,51 @@ namespace osu.Game
private RatioAdjust ratioContainer;
public CursorContainer Cursor;
protected CursorContainer Cursor;
public readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() };
public bool IsDeployedBuild => AssemblyName.Version.Major > 0;
public bool IsDebug
{
get
{
// ReSharper disable once RedundantAssignment
bool isDebug = false;
// Debug.Assert conditions are only evaluated in debug mode
Debug.Assert(isDebug = true);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
return isDebug;
}
}
public string Version
{
get
{
if (!IsDeployedBuild)
return @"local " + (IsDebug ? @"debug" : @"release");
var assembly = AssemblyName;
return $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}";
}
}
public OsuGameBase()
{
Name = @"osu!lazer";
}
[BackgroundDependencyLoader]
private void load()
{
Dependencies.Cache(this);
Dependencies.Cache(LocalConfig);
Dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, Host));
Dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, Host, BeatmapDatabase));
Dependencies.Cache(new OsuColour());
//this completely overrides the framework default. will need to change once we make a proper FontStore.

View File

@ -27,7 +27,7 @@ namespace osu.Game.Overlays
{
public class ChatOverlay : FocusedOverlayContainer, IOnlineComponent
{
const float textbox_height = 40;
private const float textbox_height = 40;
private ScheduledDelegate messageRequest;

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;

View File

@ -1,8 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;

View File

@ -1,11 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
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.Overlays.Dialog;
using OpenTK.Graphics;

View File

@ -34,7 +34,7 @@ namespace osu.Game.Overlays
Children = new Drawable[]
{
fill = new Box()
fill = new Box
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,

View File

@ -13,11 +13,11 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays
{
class LoginOverlay : FocusedOverlayContainer
internal class LoginOverlay : FocusedOverlayContainer
{
private LoginOptions optionsSection;
const float transition_time = 400;
private const float transition_time = 400;
public LoginOverlay()
{

View File

@ -4,17 +4,19 @@
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes;
namespace osu.Game.Overlays.Mods
{
public class AssistedSection : ModSection
{
protected override Key[] ToggleKeys => new Key[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
protected override Key[] ToggleKeys => new[] { Key.Z, Key.X, Key.C, Key.V, Key.B, Key.N, Key.M };
public override ModType ModType => ModType.Special;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Blue;
ButtonColour = colours.Blue;
SelectedColour = colours.BlueLight;
}

View File

@ -4,17 +4,19 @@
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes;
namespace osu.Game.Overlays.Mods
{
public class DifficultyIncreaseSection : ModSection
{
protected override Key[] ToggleKeys => new Key[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L };
protected override Key[] ToggleKeys => new[] { Key.A, Key.S, Key.D, Key.F, Key.G, Key.H, Key.J, Key.K, Key.L };
public override ModType ModType => ModType.DifficultyIncrease;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Yellow;
ButtonColour = colours.Yellow;
SelectedColour = colours.YellowLight;
}

View File

@ -4,17 +4,19 @@
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes;
namespace osu.Game.Overlays.Mods
{
public class DifficultyReductionSection : ModSection
{
protected override Key[] ToggleKeys => new Key[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P };
protected override Key[] ToggleKeys => new[] { Key.Q, Key.W, Key.E, Key.R, Key.T, Key.Y, Key.U, Key.I, Key.O, Key.P };
public override ModType ModType => ModType.DifficultyReduction;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Green;
ButtonColour = colours.Green;
SelectedColour = colours.GreenLight;
}

View File

@ -1,4 +1,4 @@
// Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@ -9,7 +9,6 @@ using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
@ -23,61 +22,57 @@ namespace osu.Game.Overlays.Mods
{
public class ModButton : FillFlowContainer
{
private ModIcon[] icons;
private ModIcon displayIcon => icons[icons.Length - 1];
private ModIcon foregroundIcon { get; set; }
private SpriteText text;
private Container iconsContainer;
private Container<ModIcon> iconsContainer;
private SampleChannel sampleOn, sampleOff;
public Action<Mod> Action; // Passed the selected mod or null if none
private int _selectedMod = -1;
private int selectedMod
private int _selectedIndex = -1;
private int selectedIndex
{
get
{
return _selectedMod;
return _selectedIndex;
}
set
{
if (value == _selectedMod) return;
_selectedMod = value;
if (value == _selectedIndex) return;
_selectedIndex = value;
if (value >= Mods.Length)
{
_selectedMod = -1;
_selectedIndex = -1;
}
else if (value <= -2)
{
_selectedMod = Mods.Length - 1;
_selectedIndex = Mods.Length - 1;
}
iconsContainer.RotateTo(Selected ? 5f : 0f, 300, EasingTypes.OutElastic);
iconsContainer.ScaleTo(Selected ? 1.1f : 1f, 300, EasingTypes.OutElastic);
for (int i = 0; i < icons.Length; i++)
{
if (Selected && i == icons.Length - 1) icons[i].Colour = SelectedColour;
else icons[i].Colour = Colour;
}
foregroundIcon.Colour = Selected ? SelectedColour : ButtonColour;
displaySelectedMod();
if (mod != null)
displayMod(SelectedMod ?? Mods[0]);
}
}
public bool Selected => selectedMod != -1;
public bool Selected => selectedIndex != -1;
private Color4 backgroundColour;
public new Color4 Colour
private Color4 buttonColour;
public Color4 ButtonColour
{
get
{
return backgroundColour;
return buttonColour;
}
set
{
if (value == backgroundColour) return;
backgroundColour = value;
foreach (ModIcon icon in icons)
if (value == buttonColour) return;
buttonColour = value;
foreach (ModIcon icon in iconsContainer.Children)
{
icon.Colour = value;
}
@ -95,7 +90,7 @@ namespace osu.Game.Overlays.Mods
{
if (value == selectedColour) return;
selectedColour = value;
if (Selected) icons[0].Colour = value;
if (Selected) foregroundIcon.Colour = value;
}
}
@ -108,30 +103,32 @@ namespace osu.Game.Overlays.Mods
}
set
{
if (mod == value) return;
mod = value;
if (mod is MultiMod)
if (mod == null)
{
mods = ((MultiMod)mod).Mods;
Mods = new Mod[0];
Alpha = 0;
}
else
{
mods = new Mod[] { mod };
Mods = (mod as MultiMod)?.Mods ?? new[] { mod };
Alpha = 1;
}
createIcons();
if (mods.Length > 0)
if (Mods.Length > 0)
{
displayMod(mods[0]);
displayMod(Mods[0]);
}
}
}
private Mod[] mods;
public Mod[] Mods => mods; // the mods from Mod, only multiple if Mod is a MultiMod
public Mod[] Mods { get; private set; }
public Mod SelectedMod => Mods.ElementAtOrDefault(selectedMod);
// the mods from Mod, only multiple if Mod is a MultiMod
public Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex);
[BackgroundDependencyLoader]
private void load(AudioManager audio)
@ -142,67 +139,47 @@ namespace osu.Game.Overlays.Mods
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
(args.Button == MouseButton.Right ? (Action)SelectPrevious : SelectNext)();
switch (args.Button)
{
case MouseButton.Left:
SelectNext();
break;
case MouseButton.Right:
SelectPrevious();
break;
}
return true;
}
public void SelectNext()
{
selectedMod++;
if (selectedMod == -1)
{
sampleOff.Play();
}
else
{
sampleOn.Play();
}
(++selectedIndex == -1 ? sampleOff : sampleOn).Play();
Action?.Invoke(SelectedMod);
}
public void SelectPrevious()
{
selectedMod--;
if (selectedMod == -1)
{
sampleOff.Play();
}
else
{
sampleOn.Play();
}
(--selectedIndex == -1 ? sampleOff : sampleOn).Play();
Action?.Invoke(SelectedMod);
}
public void Deselect()
{
selectedMod = -1;
selectedIndex = -1;
}
private void displayMod(Mod mod)
{
displayIcon.Icon = mod.Icon;
text.Text = mod.Name.GetDescription();
}
private void displaySelectedMod()
{
var modIndex = selectedMod;
if (modIndex <= -1)
{
modIndex = 0;
}
displayMod(Mods[modIndex]);
foregroundIcon.Icon = mod.Icon;
text.Text = mod.Name;
}
private void createIcons()
{
iconsContainer.Clear();
if (Mods.Length > 1)
{
iconsContainer.Add(icons = new ModIcon[]
iconsContainer.Add(new[]
{
new ModIcon
{
@ -210,35 +187,43 @@ namespace osu.Game.Overlays.Mods
Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Position = new Vector2(1.5f),
Colour = ButtonColour
},
new ModIcon
foregroundIcon = new ModIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both,
AutoSizeAxes = Axes.Both,
Position = new Vector2(-1.5f),
Colour = ButtonColour
},
});
}
else
{
iconsContainer.Add(icons = new ModIcon[]
iconsContainer.Add(foregroundIcon = new ModIcon
{
new ModIcon
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both,
},
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Colour = ButtonColour
});
}
}
protected override void LoadComplete()
{
base.LoadComplete();
foreach (ModIcon icon in iconsContainer.Children)
icon.Colour = ButtonColour;
}
public ModButton(Mod m)
{
Direction = FillDirection.Vertical;
Spacing = new Vector2(0f, -5f);
Size = new Vector2(100f);
AlwaysPresent = true;
Children = new Drawable[]
{
@ -249,7 +234,7 @@ namespace osu.Game.Overlays.Mods
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
iconsContainer = new Container
iconsContainer = new Container<ModIcon>
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,

View File

@ -2,8 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
@ -16,53 +14,29 @@ using osu.Game.Modes;
namespace osu.Game.Overlays.Mods
{
class AlwaysPresentFlowContainer : FillFlowContainer
{
public override bool IsPresent => true;
}
public class ModSection : Container
public abstract class ModSection : Container
{
private OsuSpriteText headerLabel;
private AlwaysPresentFlowContainer buttonsContainer;
public FillFlowContainer ButtonsContainer => buttonsContainer;
public FillFlowContainer<ModButton> ButtonsContainer { get; }
public Action<Mod> Action;
protected virtual Key[] ToggleKeys => new Key[] { };
protected abstract Key[] ToggleKeys { get; }
public abstract ModType ModType { get; }
public Mod[] SelectedMods
{
get
{
List<Mod> selectedMods = new List<Mod>();
foreach (ModButton button in Buttons)
{
Mod selectedMod = button.SelectedMod;
if (selectedMod != null)
selectedMods.Add(selectedMod);
}
return selectedMods.ToArray();
}
}
private string header;
public string Header
{
get
{
return header;
return headerLabel.Text;
}
set
{
header = value;
headerLabel.Text = value;
}
}
private ModButton[] buttons = {};
private ModButton[] buttons = { };
public ModButton[] Buttons
{
get
@ -76,30 +50,30 @@ namespace osu.Game.Overlays.Mods
foreach (ModButton button in value)
{
button.Colour = Colour;
button.ButtonColour = ButtonColour;
button.SelectedColour = selectedColour;
button.Action = buttonPressed;
button.Action = Action;
}
buttonsContainer.Add(value);
ButtonsContainer.Children = value;
}
}
private Color4 colour = Color4.White;
new public Color4 Colour
private Color4 buttonsBolour = Color4.White;
public Color4 ButtonColour
{
get
{
return colour;
return buttonsBolour;
}
set
{
if (value == colour) return;
colour = value;
if (value == buttonsBolour) return;
buttonsBolour = value;
foreach (ModButton button in buttons)
{
button.Colour = value;
button.ButtonColour = value;
}
}
}
@ -140,12 +114,7 @@ namespace osu.Game.Overlays.Mods
}
}
private void buttonPressed(Mod mod)
{
Action?.Invoke(mod);
}
public ModSection()
protected ModSection()
{
AutoSizeAxes = Axes.Y;
@ -156,10 +125,9 @@ namespace osu.Game.Overlays.Mods
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
Position = new Vector2(0f, 0f),
Font = @"Exo2.0-Bold",
Text = Header,
Font = @"Exo2.0-Bold"
},
buttonsContainer = new AlwaysPresentFlowContainer
ButtonsContainer = new FillFlowContainer<ModButton>
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.BottomLeft,
@ -169,6 +137,7 @@ namespace osu.Game.Overlays.Mods
{
Top = 6,
},
AlwaysPresent = true
},
};
}

View File

@ -2,11 +2,12 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
@ -33,44 +34,19 @@ namespace osu.Game.Overlays.Mods
private FillFlowContainer<ModSection> modSectionsContainer;
public readonly Bindable<Mod[]> SelectedMods = new Bindable<Mod[]>();
public readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>();
public readonly Bindable<PlayMode> PlayMode = new Bindable<PlayMode>();
private void modeChanged(object sender, EventArgs eventArgs)
{
var ruleset = Ruleset.GetRuleset(PlayMode);
modSectionsContainer.Children = new ModSection[]
{
new DifficultyReductionSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
Buttons = ruleset.GetModsFor(ModType.DifficultyReduction).Select(m => new ModButton(m)).ToArray(),
},
new DifficultyIncreaseSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
Buttons = ruleset.GetModsFor(ModType.DifficultyIncrease).Select(m => new ModButton(m)).ToArray(),
},
new AssistedSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
Buttons = ruleset.GetModsFor(ModType.Special).Select(m => new ModButton(m)).ToArray(),
},
};
foreach (ModSection section in modSectionsContainer.Children)
section.Buttons = ruleset.GetModsFor(section.ModType).Select(m => new ModButton(m)).ToArray();
refreshSelectedMods();
}
[BackgroundDependencyLoader(permitNulls:true)]
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu)
{
lowMultiplierColour = colours.Red;
@ -79,7 +55,7 @@ namespace osu.Game.Overlays.Mods
if (osu != null)
PlayMode.BindTo(osu.PlayMode);
PlayMode.ValueChanged += modeChanged;
PlayMode.TriggerChange();
modeChanged(null, null);
}
protected override void PopOut()
@ -115,43 +91,33 @@ namespace osu.Game.Overlays.Mods
public void DeselectAll()
{
foreach (ModSection section in modSectionsContainer.Children)
{
foreach (ModButton button in section.Buttons)
{
button.Deselect();
}
}
section.DeselectAll();
}
public void DeselectMod(Modes.Mods modName)
public void DeselectTypes(Type[] modTypes)
{
if (modTypes.Length == 0) return;
foreach (ModSection section in modSectionsContainer.Children)
{
foreach (ModButton button in section.Buttons)
{
foreach (Mod mod in button.Mods)
{
if (mod.Name == modName)
{
Mod selected = button.SelectedMod;
if (selected == null) continue;
foreach (Type type in modTypes)
if (type.IsInstanceOfType(selected))
button.Deselect();
return;
}
}
}
}
}
private void modButtonPressed(Mod selectedMod)
{
if (selectedMod != null)
{
foreach (Modes.Mods disableMod in selectedMod.DisablesMods)
{
DeselectMod(disableMod);
}
}
DeselectTypes(selectedMod.IncompatibleMods);
refreshSelectedMods();
}
private void refreshSelectedMods()
{
SelectedMods.Value = modSectionsContainer.Children.SelectMany(s => s.Buttons.Select(x => x.SelectedMod).Where(x => x != null)).ToArray();
double multiplier = 1.0;
bool ranked = true;
@ -159,46 +125,23 @@ namespace osu.Game.Overlays.Mods
foreach (Mod mod in SelectedMods.Value)
{
multiplier *= mod.ScoreMultiplier;
if (ranked)
ranked = mod.Ranked;
ranked &= mod.Ranked;
}
// 1.00x
// 1.05x
// 1.20x
multiplierLabel.Text = string.Format("{0:N2}x", multiplier);
multiplierLabel.Text = $"{multiplier:N2}x";
string rankedString = ranked ? "Ranked" : "Unranked";
rankedLabel.Text = $@"{rankedString}, Score Multiplier: ";
if (multiplier > 1.0)
{
multiplierLabel.FadeColour(highMultiplierColour, 200);
}
else if (multiplier < 1.0)
{
multiplierLabel.FadeColour(lowMultiplierColour, 200);
}
else
{
multiplierLabel.FadeColour(Color4.White, 200);
}
}
private void refreshSelectedMods()
{
List<Mod> selectedMods = new List<Mod>();
foreach (ModSection section in modSectionsContainer.Children)
{
foreach (Mod mod in section.SelectedMods)
{
selectedMods.Add(mod);
}
}
SelectedMods.Value = selectedMods.ToArray();
}
public ModSelectOverlay()
@ -309,6 +252,30 @@ namespace osu.Game.Overlays.Mods
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Width = content_width,
Children = new ModSection[]
{
new DifficultyReductionSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
new DifficultyIncreaseSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
new AssistedSection
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Action = modButtonPressed,
},
}
},
// Footer
new Container

View File

@ -3,11 +3,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@ -23,6 +23,7 @@ using osu.Game.Database;
using osu.Game.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Overlays
{
@ -30,7 +31,7 @@ namespace osu.Game.Overlays
{
private MusicControllerBackground backgroundSprite;
private DragBar progress;
private TextAwesome playButton, listButton;
private TextAwesome playButton;
private SpriteText title, artist;
private List<BeatmapSetInfo> playList;
@ -59,7 +60,9 @@ namespace osu.Game.Overlays
protected override bool OnDrag(InputState state)
{
Vector2 change = (state.Mouse.Position - state.Mouse.PositionMouseDown.Value);
Trace.Assert(state.Mouse.PositionMouseDown != null, "state.Mouse.PositionMouseDown != null");
Vector2 change = state.Mouse.Position - state.Mouse.PositionMouseDown.Value;
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
change *= (float)Math.Pow(change.Length, 0.7f) / change.Length;
@ -187,7 +190,7 @@ namespace osu.Game.Overlays
Position = new Vector2(20, -30),
Children = new Drawable[]
{
listButton = new TextAwesome
new TextAwesome
{
TextSize = 15,
Icon = FontAwesome.fa_bars,
@ -246,14 +249,14 @@ namespace osu.Game.Overlays
}
}
void preferUnicode_changed(object sender, EventArgs e)
private void preferUnicode_changed(object sender, EventArgs e)
{
updateDisplay(current, TransformDirection.None);
}
private void workingChanged(object sender = null, EventArgs e = null)
{
progress.IsEnabled = (beatmapSource.Value != null);
progress.IsEnabled = beatmapSource.Value != null;
if (beatmapSource.Value == current) return;
bool audioEquals = current?.BeatmapInfo?.AudioEquals(beatmapSource?.Value?.BeatmapInfo) ?? false;
current = beatmapSource.Value;
@ -323,7 +326,7 @@ namespace osu.Game.Overlays
updateDisplay(current, isNext ? TransformDirection.Next : TransformDirection.Prev);
}
Action pendingBeatmapSwitch;
private Action pendingBeatmapSwitch;
private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction)
{
@ -384,7 +387,7 @@ namespace osu.Game.Overlays
base.Dispose(isDisposing);
}
const float transition_length = 800;
private const float transition_length = 800;
protected override void PopIn()
{

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics;
using osu.Game.Overlays.Notifications;
using OpenTK.Graphics;
@ -70,7 +69,7 @@ namespace osu.Game.Overlays
};
}
int runningDepth = 0;
private int runningDepth;
public void Post(Notification notification)
{

View File

@ -3,6 +3,7 @@
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
@ -42,18 +43,9 @@ namespace osu.Game.Overlays.Notifications
protected Container NotificationContent;
private bool read;
public virtual bool Read { get; set; }
public virtual bool Read
{
get { return read; }
set
{
read = value;
}
}
public Notification()
protected Notification()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
@ -161,7 +153,7 @@ namespace osu.Game.Overlays.Notifications
Expire();
}
class CloseButton : ClickableContainer
private class CloseButton : ClickableContainer
{
private Color4 hoverColour;
@ -175,6 +167,7 @@ namespace osu.Game.Overlays.Notifications
new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.fa_times_circle,
}
};

View File

@ -133,7 +133,7 @@ namespace osu.Game.Overlays.Notifications
countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString();
}
class ClearAllButton : ClickableContainer
private class ClearAllButton : ClickableContainer
{
private OsuSpriteText text;

View File

@ -10,11 +10,8 @@ namespace osu.Game.Overlays.Notifications
{
public class ProgressCompletionNotification : SimpleNotification
{
private ProgressNotification progressNotification;
public ProgressCompletionNotification(ProgressNotification progressNotification)
public ProgressCompletionNotification()
{
this.progressNotification = progressNotification;
Icon = FontAwesome.fa_check;
}

View File

@ -90,7 +90,7 @@ namespace osu.Game.Overlays.Notifications
private ProgressNotificationState state;
protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification(this)
protected virtual Notification CreateCompletionNotification() => new ProgressCompletionNotification()
{
Activated = CompletionClickAction,
Text = $"Task \"{Text}\" has completed!"
@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Notifications
/// </summary>
public Func<bool> CompletionClickAction;
class ProgressBar : Container
private class ProgressBar : Container
{
private Box box;

View File

@ -51,6 +51,7 @@ namespace osu.Game.Overlays.Notifications
iconDrawable = new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
}
});
@ -80,10 +81,8 @@ namespace osu.Game.Overlays.Notifications
set
{
if (base.Read = value)
Light.FadeOut(100);
else
Light.FadeIn(100);
base.Read = value;
Light.FadeTo(value ? 1 : 0, 100);
}
}
}

View File

@ -39,19 +39,19 @@ namespace osu.Game.Overlays.Options
bindable.ValueChanged += bindable_ValueChanged;
bindable_ValueChanged(null, null);
if (bindable?.Disabled ?? true)
if (bindable.Disabled)
Alpha = 0.3f;
}
}
private Bindable<T> bindable;
void bindable_ValueChanged(object sender, EventArgs e)
private void bindable_ValueChanged(object sender, EventArgs e)
{
dropdown.SelectedValue = bindable.Value;
}
void dropdown_ValueChanged(object sender, EventArgs e)
private void dropdown_ValueChanged(object sender, EventArgs e)
{
bindable.Value = dropdown.SelectedValue;
}

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