mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 07:06:35 +09:00
Merge branch 'master' into online-beatmap-set-overlay
This commit is contained in:
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Storyboards;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -40,6 +41,11 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
|
||||
|
||||
/// <summary>
|
||||
/// The Beatmap's Storyboard.
|
||||
/// </summary>
|
||||
public Storyboard Storyboard = new Storyboard();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new beatmap.
|
||||
/// </summary>
|
||||
@ -51,6 +57,7 @@ namespace osu.Game.Beatmaps
|
||||
Breaks = original?.Breaks ?? Breaks;
|
||||
ComboColors = original?.ComboColors ?? ComboColors;
|
||||
HitObjects = original?.HitObjects ?? HitObjects;
|
||||
Storyboard = original?.Storyboard ?? Storyboard;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@ -62,11 +61,6 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => null;
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string Description => "dummy";
|
||||
|
||||
public DummyRuleset(RulesetInfo rulesetInfo)
|
||||
|
@ -10,6 +10,10 @@ using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Storyboards;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.IO.File;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
@ -238,42 +242,231 @@ namespace osu.Game.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEvents(Beatmap beatmap, string line)
|
||||
private void handleEvents(Beatmap beatmap, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup)
|
||||
{
|
||||
var depth = 0;
|
||||
while (line.StartsWith(" ") || line.StartsWith("_"))
|
||||
{
|
||||
++depth;
|
||||
line = line.Substring(1);
|
||||
}
|
||||
|
||||
decodeVariables(ref line);
|
||||
|
||||
string[] split = line.Split(',');
|
||||
|
||||
EventType type;
|
||||
if (!Enum.TryParse(split[0], out type))
|
||||
throw new InvalidDataException($@"Unknown event type {split[0]}");
|
||||
|
||||
// Todo: Implement the rest
|
||||
switch (type)
|
||||
if (depth == 0)
|
||||
{
|
||||
case EventType.Video:
|
||||
case EventType.Background:
|
||||
string filename = split[2].Trim('"');
|
||||
storyboardSprite = null;
|
||||
|
||||
if (type == EventType.Background)
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
|
||||
EventType type;
|
||||
if (!Enum.TryParse(split[0], out type))
|
||||
throw new InvalidDataException($@"Unknown event type {split[0]}");
|
||||
|
||||
break;
|
||||
case EventType.Break:
|
||||
var breakEvent = new BreakPeriod
|
||||
{
|
||||
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
|
||||
EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
|
||||
};
|
||||
switch (type)
|
||||
{
|
||||
case EventType.Video:
|
||||
case EventType.Background:
|
||||
string filename = split[2].Trim('"');
|
||||
|
||||
if (!breakEvent.HasEffect)
|
||||
return;
|
||||
if (type == EventType.Background)
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
|
||||
|
||||
beatmap.Breaks.Add(breakEvent);
|
||||
break;
|
||||
break;
|
||||
case EventType.Break:
|
||||
var breakEvent = new BreakPeriod
|
||||
{
|
||||
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
|
||||
EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
|
||||
};
|
||||
|
||||
if (!breakEvent.HasEffect)
|
||||
return;
|
||||
|
||||
beatmap.Breaks.Add(breakEvent);
|
||||
break;
|
||||
case EventType.Sprite:
|
||||
{
|
||||
var layer = parseLayer(split[1]);
|
||||
var origin = parseOrigin(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||
storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
|
||||
beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite);
|
||||
}
|
||||
break;
|
||||
case EventType.Animation:
|
||||
{
|
||||
var layer = parseLayer(split[1]);
|
||||
var origin = parseOrigin(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||
var frameCount = int.Parse(split[6]);
|
||||
var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo);
|
||||
var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever;
|
||||
storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType);
|
||||
beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite);
|
||||
}
|
||||
break;
|
||||
case EventType.Sample:
|
||||
{
|
||||
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||
var layer = parseLayer(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
|
||||
beatmap.Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (depth < 2)
|
||||
timelineGroup = storyboardSprite?.TimelineGroup;
|
||||
|
||||
var commandType = split[0];
|
||||
switch (commandType)
|
||||
{
|
||||
case "T":
|
||||
{
|
||||
var triggerName = split[1];
|
||||
var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue;
|
||||
var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue;
|
||||
var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0;
|
||||
timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber);
|
||||
}
|
||||
break;
|
||||
case "L":
|
||||
{
|
||||
var startTime = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||
var loopCount = int.Parse(split[2]);
|
||||
timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (string.IsNullOrEmpty(split[3]))
|
||||
split[3] = split[2];
|
||||
|
||||
var easing = (Easing)int.Parse(split[1]);
|
||||
var startTime = double.Parse(split[2], CultureInfo.InvariantCulture);
|
||||
var endTime = double.Parse(split[3], CultureInfo.InvariantCulture);
|
||||
|
||||
switch (commandType)
|
||||
{
|
||||
case "F":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "S":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue));
|
||||
}
|
||||
break;
|
||||
case "V":
|
||||
{
|
||||
var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
||||
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
|
||||
}
|
||||
break;
|
||||
case "R":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue));
|
||||
}
|
||||
break;
|
||||
case "M":
|
||||
{
|
||||
var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
||||
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
||||
timelineGroup?.X.Add(easing, startTime, endTime, startX, endX);
|
||||
timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY);
|
||||
}
|
||||
break;
|
||||
case "MX":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "MY":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "C":
|
||||
{
|
||||
var startRed = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture);
|
||||
var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed;
|
||||
var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen;
|
||||
var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue;
|
||||
timelineGroup?.Colour.Add(easing, startTime, endTime,
|
||||
new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1),
|
||||
new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1));
|
||||
}
|
||||
break;
|
||||
case "P":
|
||||
{
|
||||
var type = split[4];
|
||||
switch (type)
|
||||
{
|
||||
case "A": timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); break;
|
||||
case "H": timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); break;
|
||||
case "V": timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDataException($@"Unknown command type: {commandType}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string cleanFilename(string path)
|
||||
=> FileSafety.PathStandardise(path.Trim('\"'));
|
||||
|
||||
private static Anchor parseOrigin(string value)
|
||||
{
|
||||
var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value);
|
||||
switch (origin)
|
||||
{
|
||||
case LegacyOrigins.TopLeft: return Anchor.TopLeft;
|
||||
case LegacyOrigins.TopCentre: return Anchor.TopCentre;
|
||||
case LegacyOrigins.TopRight: return Anchor.TopRight;
|
||||
case LegacyOrigins.CentreLeft: return Anchor.CentreLeft;
|
||||
case LegacyOrigins.Centre: return Anchor.Centre;
|
||||
case LegacyOrigins.CentreRight: return Anchor.CentreRight;
|
||||
case LegacyOrigins.BottomLeft: return Anchor.BottomLeft;
|
||||
case LegacyOrigins.BottomCentre: return Anchor.BottomCentre;
|
||||
case LegacyOrigins.BottomRight: return Anchor.BottomRight;
|
||||
}
|
||||
throw new InvalidDataException($@"Unknown origin: {value}");
|
||||
}
|
||||
|
||||
private static string parseLayer(string value)
|
||||
=> Enum.Parse(typeof(StoryLayer), value).ToString();
|
||||
|
||||
private void handleTimingPoints(Beatmap beatmap, string line)
|
||||
{
|
||||
string[] split = line.Split(',');
|
||||
@ -414,6 +607,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
Section section = Section.None;
|
||||
bool hasCustomColours = false;
|
||||
StoryboardSprite storyboardSprite = null;
|
||||
CommandTimelineGroup timelineGroup = null;
|
||||
|
||||
string line;
|
||||
while ((line = stream.ReadLine()) != null)
|
||||
@ -421,7 +616,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
|
||||
if (line.StartsWith(" ") || line.StartsWith("_") || line.StartsWith("//"))
|
||||
if (line.StartsWith("//"))
|
||||
continue;
|
||||
|
||||
if (line.StartsWith(@"osu file format v"))
|
||||
@ -452,7 +647,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
handleDifficulty(beatmap, line);
|
||||
break;
|
||||
case Section.Events:
|
||||
handleEvents(beatmap, line);
|
||||
handleEvents(beatmap, line, ref storyboardSprite, ref timelineGroup);
|
||||
break;
|
||||
case Section.TimingPoints:
|
||||
handleTimingPoints(beatmap, line);
|
||||
@ -509,5 +704,27 @@ namespace osu.Game.Beatmaps.Formats
|
||||
Sample = 5,
|
||||
Animation = 6
|
||||
}
|
||||
|
||||
internal enum LegacyOrigins
|
||||
{
|
||||
TopLeft,
|
||||
Centre,
|
||||
CentreLeft,
|
||||
TopRight,
|
||||
BottomCentre,
|
||||
TopCentre,
|
||||
Custom,
|
||||
CentreRight,
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
};
|
||||
|
||||
internal enum StoryLayer
|
||||
{
|
||||
Background = 0,
|
||||
Fail = 1,
|
||||
Pass = 2,
|
||||
Foreground = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +100,11 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public void TransferTo(WorkingBeatmap other)
|
||||
{
|
||||
if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
||||
other.track = track;
|
||||
lock (trackLock)
|
||||
{
|
||||
if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
||||
other.track = track;
|
||||
}
|
||||
|
||||
if (background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
|
||||
other.background = background;
|
||||
|
@ -228,9 +228,9 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
var size = new Vector2(2 * offset.X, offset.Y);
|
||||
|
||||
var triangle = new Triangle(
|
||||
particle.Position * Size * DrawInfo.Matrix,
|
||||
(particle.Position * Size + offset) * DrawInfo.Matrix,
|
||||
(particle.Position * Size + new Vector2(-offset.X, offset.Y)) * DrawInfo.Matrix
|
||||
Vector2Extensions.Transform(particle.Position * Size, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(particle.Position * Size + offset, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix)
|
||||
);
|
||||
|
||||
ColourInfo colourInfo = DrawInfo.Colour;
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Graphics.Cursor
|
||||
|
||||
// don't start rotating until we're moved a minimum distance away from the mouse down location,
|
||||
// else it can have an annoying effect.
|
||||
startRotation |= Vector2.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
|
||||
startRotation |= Vector2Extensions.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
|
||||
|
||||
if (startRotation)
|
||||
{
|
||||
|
@ -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 System.Collections.Generic;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@ -112,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
public string[] FilterTerms => new[] { Text };
|
||||
public IEnumerable<string> FilterTerms => new[] { Text };
|
||||
|
||||
public bool MatchingFilter
|
||||
{
|
||||
|
25
osu.Game/OpenTK.dll.config
Normal file
25
osu.Game/OpenTK.dll.config
Normal file
@ -0,0 +1,25 @@
|
||||
<configuration>
|
||||
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
|
||||
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
|
||||
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
|
||||
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
|
||||
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
|
||||
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
|
||||
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
|
||||
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
|
||||
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
|
||||
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
|
||||
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
|
||||
<!-- XQuartz compatibility (X11 on Mac) -->
|
||||
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
|
||||
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
|
||||
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
|
||||
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
|
||||
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
|
||||
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
|
||||
</configuration>
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Chat
|
||||
private Color4 topicColour;
|
||||
private Color4 hoverColour;
|
||||
|
||||
public string[] FilterTerms => new[] { channel.Name };
|
||||
public IEnumerable<string> FilterTerms => new[] { channel.Name };
|
||||
public bool MatchingFilter
|
||||
{
|
||||
set
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Chat
|
||||
public readonly FillFlowContainer<ChannelListItem> ChannelFlow;
|
||||
|
||||
public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children;
|
||||
public string[] FilterTerms => new[] { Header };
|
||||
public IEnumerable<string> FilterTerms => new[] { Header };
|
||||
public bool MatchingFilter
|
||||
{
|
||||
set
|
||||
|
@ -12,8 +12,10 @@ using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
@ -56,7 +58,7 @@ namespace osu.Game.Overlays
|
||||
private readonly Box chatBackground;
|
||||
private readonly Box tabBackground;
|
||||
|
||||
private Bindable<double> chatHeight;
|
||||
public Bindable<double> ChatHeight { get; internal set; }
|
||||
|
||||
private readonly Container channelSelectionContainer;
|
||||
private readonly ChannelSelectionOverlay channelSelection;
|
||||
@ -177,18 +179,11 @@ namespace osu.Game.Overlays
|
||||
if (state == Visibility.Visible)
|
||||
{
|
||||
textbox.HoldFocus = false;
|
||||
if (1f - chatHeight.Value < channel_selection_min_height)
|
||||
{
|
||||
chatContainer.ResizeHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint);
|
||||
channelSelectionContainer.ResizeHeightTo(channel_selection_min_height, 800, Easing.OutQuint);
|
||||
channelSelection.Show();
|
||||
chatHeight.Value = 1f - channel_selection_min_height;
|
||||
}
|
||||
if (1f - ChatHeight.Value < channel_selection_min_height)
|
||||
transformChatHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
textbox.HoldFocus = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -202,7 +197,7 @@ namespace osu.Game.Overlays
|
||||
if (!isDragging)
|
||||
return base.OnDragStart(state);
|
||||
|
||||
startDragChatHeight = chatHeight.Value;
|
||||
startDragChatHeight = ChatHeight.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -212,7 +207,13 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
Trace.Assert(state.Mouse.PositionMouseDown != null);
|
||||
|
||||
chatHeight.Value = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
|
||||
double targetChatHeight = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
|
||||
|
||||
// If the channel selection screen is shown, mind its minimum height
|
||||
if (channelSelection.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
|
||||
targetChatHeight = 1f - channel_selection_min_height;
|
||||
|
||||
ChatHeight.Value = targetChatHeight;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -272,14 +273,14 @@ namespace osu.Game.Overlays
|
||||
this.api = api;
|
||||
api.Register(this);
|
||||
|
||||
chatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
||||
chatHeight.ValueChanged += h =>
|
||||
ChatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
||||
ChatHeight.ValueChanged += h =>
|
||||
{
|
||||
chatContainer.Height = (float)h;
|
||||
channelSelectionContainer.Height = 1f - (float)h;
|
||||
tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200);
|
||||
};
|
||||
chatHeight.TriggerChange();
|
||||
ChatHeight.TriggerChange();
|
||||
|
||||
chatBackground.Colour = colours.ChatBlue;
|
||||
}
|
||||
@ -501,5 +502,26 @@ namespace osu.Game.Overlays
|
||||
|
||||
api.Queue(req);
|
||||
}
|
||||
|
||||
private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None)
|
||||
{
|
||||
this.TransformTo(this.PopulateTransform(new TransformChatHeight(), newChatHeight, duration, easing));
|
||||
}
|
||||
|
||||
private class TransformChatHeight : Transform<double, ChatOverlay>
|
||||
{
|
||||
private double valueAt(double time)
|
||||
{
|
||||
if (time < StartTime) return StartValue;
|
||||
if (time >= EndTime) return EndValue;
|
||||
|
||||
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||
}
|
||||
|
||||
public override string TargetMember => "ChatHeight.Value";
|
||||
|
||||
protected override void Apply(ChatOverlay d, double time) => d.ChatHeight.Value = valueAt(time);
|
||||
protected override void ReadIntoStartValue(ChatOverlay d) => StartValue = d.ChatHeight.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
|
||||
private FillFlowContainer<KeyButton> buttons;
|
||||
|
||||
public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
|
||||
public IEnumerable<string> FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
|
||||
|
||||
public KeyBindingRow(object action, IEnumerable<Framework.Input.Bindings.KeyBinding> bindings)
|
||||
{
|
||||
@ -371,4 +371,4 @@ namespace osu.Game.Overlays.KeyBinding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Music
|
||||
return true;
|
||||
}
|
||||
|
||||
public string[] FilterTerms { get; private set; }
|
||||
public IEnumerable<string> FilterTerms { get; private set; }
|
||||
|
||||
private bool matching = true;
|
||||
|
||||
|
@ -229,7 +229,7 @@ namespace osu.Game.Overlays.Music
|
||||
|
||||
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
|
||||
{
|
||||
public string[] FilterTerms => new string[] { };
|
||||
public IEnumerable<string> FilterTerms => new string[] { };
|
||||
public bool MatchingFilter
|
||||
{
|
||||
set
|
||||
|
@ -414,8 +414,8 @@ namespace osu.Game.Overlays.Profile
|
||||
scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0")));
|
||||
scoreText.Add(createScoreText("Max Combo"));
|
||||
scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0")));
|
||||
scoreText.Add(createScoreText("Replay Watched by Others"));
|
||||
scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplayWatched.ToString(@"#,0")));
|
||||
scoreText.Add(createScoreText("Replays Watched by Others"));
|
||||
scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0")));
|
||||
|
||||
gradeSS.DisplayCount = user.Statistics.GradesCount.SS;
|
||||
gradeSS.Show();
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Force garbage collection",
|
||||
Action = () => GC.Collect()
|
||||
Action = GC.Collect
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Open osu! folder",
|
||||
Action = () => storage.OpenInNativeExplorer(),
|
||||
Action = storage.OpenInNativeExplorer,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Text = "Key Configuration",
|
||||
Action = () => keyConfig.ToggleVisibility()
|
||||
Action = keyConfig.ToggleVisibility
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -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 System.Collections.Generic;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
@ -53,7 +54,7 @@ namespace osu.Game.Overlays.Settings
|
||||
}
|
||||
}
|
||||
|
||||
public string[] FilterTerms => new[] { LabelText };
|
||||
public IEnumerable<string> FilterTerms => new[] { LabelText };
|
||||
|
||||
public bool MatchingFilter
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings
|
||||
public abstract string Header { get; }
|
||||
|
||||
public IEnumerable<IFilterable> FilterableChildren => Children.OfType<IFilterable>();
|
||||
public string[] FilterTerms => new[] { Header };
|
||||
public IEnumerable<string> FilterTerms => new[] { Header };
|
||||
|
||||
private const int header_size = 26;
|
||||
private const int header_margin = 25;
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Settings
|
||||
protected abstract string Header { get; }
|
||||
|
||||
public IEnumerable<IFilterable> FilterableChildren => Children.OfType<IFilterable>();
|
||||
public string[] FilterTerms => new[] { Header };
|
||||
public IEnumerable<string> FilterTerms => new[] { Header };
|
||||
public bool MatchingFilter
|
||||
{
|
||||
set
|
||||
|
@ -25,6 +25,10 @@ namespace osu.Game.Rulesets.Judgements
|
||||
/// </summary>
|
||||
public double TimeOffset { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the <see cref="Result"/> should affect the combo portion of the score.
|
||||
/// If false, the <see cref="Result"/> will be considered for the bonus portion of the score.
|
||||
/// </summary>
|
||||
public virtual bool AffectsCombo => true;
|
||||
|
||||
/// <summary>
|
||||
|
@ -55,8 +55,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
|
||||
Depth = (float)hitObject.StartTime;
|
||||
}
|
||||
|
||||
private ArmedState state;
|
||||
|
@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
}
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
protected DrawableScrollingHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
|
@ -10,7 +10,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets
|
||||
@ -50,8 +49,6 @@ namespace osu.Game.Rulesets
|
||||
|
||||
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap);
|
||||
|
||||
public abstract ScoreProcessor CreateScoreProcessor();
|
||||
|
||||
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle };
|
||||
|
||||
public abstract string Description { get; }
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -15,11 +15,17 @@ namespace osu.Game.Rulesets.Scoring
|
||||
public abstract class ScoreProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when the ScoreProcessor is in a failed state.
|
||||
/// Invoked when the <see cref="ScoreProcessor"/> is in a failed state.
|
||||
/// This may occur regardless of whether an <see cref="AllJudged"/> event is invoked.
|
||||
/// Return true if the fail was permitted.
|
||||
/// </summary>
|
||||
public event Func<bool> Failed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when all <see cref="HitObject"/>s have been judged.
|
||||
/// </summary>
|
||||
public event Action AllJudged;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the <see cref="ScoreProcessor"/>.
|
||||
/// </summary>
|
||||
@ -33,7 +39,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <summary>
|
||||
/// The current accuracy.
|
||||
/// </summary>
|
||||
public readonly BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 };
|
||||
public readonly BindableDouble Accuracy = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||
|
||||
/// <summary>
|
||||
/// The current health.
|
||||
@ -50,10 +56,15 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// </summary>
|
||||
public readonly BindableInt HighestCombo = new BindableInt();
|
||||
|
||||
/// <summary>
|
||||
/// Whether all <see cref="Judgement"/>s have been processed.
|
||||
/// </summary>
|
||||
protected virtual bool HasCompleted => false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the score is in a failed state.
|
||||
/// </summary>
|
||||
public virtual bool HasFailed => false;
|
||||
public virtual bool HasFailed => Health.Value == Health.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this ScoreProcessor has already triggered the failed state.
|
||||
@ -63,8 +74,6 @@ namespace osu.Game.Rulesets.Scoring
|
||||
protected ScoreProcessor()
|
||||
{
|
||||
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
private ScoreRank rankFrom(double acc)
|
||||
@ -85,11 +94,12 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <summary>
|
||||
/// Resets this ScoreProcessor to a default state.
|
||||
/// </summary>
|
||||
protected virtual void Reset()
|
||||
/// <param name="storeResults">Whether to store the current state of the <see cref="ScoreProcessor"/> for future use.</param>
|
||||
protected virtual void Reset(bool storeResults)
|
||||
{
|
||||
TotalScore.Value = 0;
|
||||
Accuracy.Value = 0;
|
||||
Health.Value = 0;
|
||||
Accuracy.Value = 1;
|
||||
Health.Value = 1;
|
||||
Combo.Value = 0;
|
||||
HighestCombo.Value = 0;
|
||||
|
||||
@ -118,6 +128,9 @@ namespace osu.Game.Rulesets.Scoring
|
||||
protected void NotifyNewJudgement(Judgement judgement)
|
||||
{
|
||||
NewJudgement?.Invoke(judgement);
|
||||
|
||||
if (HasCompleted)
|
||||
AllJudged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -135,36 +148,55 @@ namespace osu.Game.Rulesets.Scoring
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ScoreProcessor<TObject> : ScoreProcessor
|
||||
public class ScoreProcessor<TObject> : ScoreProcessor
|
||||
where TObject : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// All judgements held by this ScoreProcessor.
|
||||
/// </summary>
|
||||
protected readonly List<Judgement> Judgements = new List<Judgement>();
|
||||
private const double base_portion = 0.3;
|
||||
private const double combo_portion = 0.7;
|
||||
private const double max_score = 1000000;
|
||||
|
||||
public override bool HasFailed => Health.Value == Health.MinValue;
|
||||
public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>();
|
||||
|
||||
protected sealed override bool HasCompleted => Hits == MaxHits;
|
||||
|
||||
protected int MaxHits { get; private set; }
|
||||
protected int Hits { get; private set; }
|
||||
|
||||
private double maxHighestCombo;
|
||||
|
||||
private double maxBaseScore;
|
||||
private double rollingMaxBaseScore;
|
||||
private double baseScore;
|
||||
|
||||
protected ScoreProcessor()
|
||||
{
|
||||
}
|
||||
|
||||
protected ScoreProcessor(RulesetContainer<TObject> rulesetContainer)
|
||||
public ScoreProcessor(RulesetContainer<TObject> rulesetContainer)
|
||||
{
|
||||
Judgements.Capacity = rulesetContainer.Beatmap.HitObjects.Count;
|
||||
Debug.Assert(base_portion + combo_portion == 1.0);
|
||||
|
||||
rulesetContainer.OnJudgement += AddJudgement;
|
||||
|
||||
ComputeTargets(rulesetContainer.Beatmap);
|
||||
SimulateAutoplay(rulesetContainer.Beatmap);
|
||||
Reset(true);
|
||||
|
||||
Reset();
|
||||
if (maxBaseScore == 0 || maxHighestCombo == 0)
|
||||
{
|
||||
Mode.Value = ScoringMode.Exponential;
|
||||
Mode.Disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes target scoring values for this ScoreProcessor. This is equivalent to performing an auto-play of the score to find the values.
|
||||
/// Simulates an autoplay of <see cref="HitObject"/>s that will be judged by this <see cref="ScoreProcessor{TObject}"/>
|
||||
/// by adding <see cref="Judgement"/>s for each <see cref="HitObject"/> in the <see cref="Beatmap{TObject}"/>.
|
||||
/// <para>
|
||||
/// This is required for <see cref="ScoringMode.Standardised"/> to work, otherwise <see cref="ScoringMode.Exponential"/> will be used.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The Beatmap containing the objects that will be judged by this ScoreProcessor.</param>
|
||||
protected virtual void ComputeTargets(Beatmap<TObject> beatmap) { }
|
||||
/// <param name="beatmap">The <see cref="Beatmap{TObject}"/> containing the <see cref="HitObject"/>s that will be judged by this <see cref="ScoreProcessor{TObject}"/>.</param>
|
||||
protected virtual void SimulateAutoplay(Beatmap<TObject> beatmap) { }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a judgement to this ScoreProcessor.
|
||||
@ -172,45 +204,72 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// <param name="judgement">The judgement to add.</param>
|
||||
protected void AddJudgement(Judgement judgement)
|
||||
{
|
||||
bool exists = Judgements.Contains(judgement);
|
||||
|
||||
if (!exists)
|
||||
{
|
||||
if (judgement.AffectsCombo)
|
||||
{
|
||||
switch (judgement.Result)
|
||||
{
|
||||
case HitResult.None:
|
||||
break;
|
||||
case HitResult.Miss:
|
||||
Combo.Value = 0;
|
||||
break;
|
||||
default:
|
||||
Combo.Value++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Judgements.Add(judgement);
|
||||
OnNewJudgement(judgement);
|
||||
|
||||
NotifyNewJudgement(judgement);
|
||||
}
|
||||
OnNewJudgement(judgement);
|
||||
NotifyNewJudgement(judgement);
|
||||
|
||||
UpdateFailed();
|
||||
}
|
||||
|
||||
protected override void Reset()
|
||||
protected virtual void OnNewJudgement(Judgement judgement)
|
||||
{
|
||||
base.Reset();
|
||||
double bonusScore = 0;
|
||||
|
||||
Judgements.Clear();
|
||||
if (judgement.AffectsCombo)
|
||||
{
|
||||
switch (judgement.Result)
|
||||
{
|
||||
case HitResult.None:
|
||||
break;
|
||||
case HitResult.Miss:
|
||||
Combo.Value = 0;
|
||||
break;
|
||||
default:
|
||||
Combo.Value++;
|
||||
break;
|
||||
}
|
||||
|
||||
baseScore += judgement.NumericResult;
|
||||
rollingMaxBaseScore += judgement.MaxNumericResult;
|
||||
|
||||
Hits++;
|
||||
}
|
||||
else if (judgement.IsHit)
|
||||
bonusScore += judgement.NumericResult;
|
||||
|
||||
if (rollingMaxBaseScore != 0)
|
||||
Accuracy.Value = baseScore / rollingMaxBaseScore;
|
||||
|
||||
switch (Mode.Value)
|
||||
{
|
||||
case ScoringMode.Standardised:
|
||||
TotalScore.Value = max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore;
|
||||
break;
|
||||
case ScoringMode.Exponential:
|
||||
TotalScore.Value = (baseScore + bonusScore) * Math.Log(HighestCombo + 1, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates any values that need post-processing. Invoked when a new judgement has occurred.
|
||||
/// </summary>
|
||||
/// <param name="judgement">The judgement that triggered this calculation.</param>
|
||||
protected abstract void OnNewJudgement(Judgement judgement);
|
||||
protected override void Reset(bool storeResults)
|
||||
{
|
||||
if (storeResults)
|
||||
{
|
||||
MaxHits = Hits;
|
||||
maxHighestCombo = HighestCombo;
|
||||
maxBaseScore = baseScore;
|
||||
}
|
||||
|
||||
base.Reset(storeResults);
|
||||
|
||||
Hits = 0;
|
||||
baseScore = 0;
|
||||
rollingMaxBaseScore = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ScoringMode
|
||||
{
|
||||
Standardised,
|
||||
Exponential
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Timing
|
||||
internal Axes ScrollingAxes;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
/// <summary>
|
||||
/// The control point that defines the speed adjustments for this container. This is set by the <see cref="SpeedAdjustmentContainer"/>.
|
||||
|
@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Timing
|
||||
}
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
protected override bool RequiresChildrenUpdate => true;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.
|
||||
|
@ -29,11 +29,6 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public abstract class RulesetContainer : Container
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when all the judgeable HitObjects have been judged.
|
||||
/// </summary>
|
||||
public event Action OnAllJudged;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to apply adjustments to the child <see cref="Playfield"/> based on our own size.
|
||||
/// </summary>
|
||||
@ -61,11 +56,6 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public abstract IEnumerable<HitObject> Objects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether all the HitObjects have been judged.
|
||||
/// </summary>
|
||||
protected abstract bool AllObjectsJudged { get; }
|
||||
|
||||
protected readonly Ruleset Ruleset;
|
||||
|
||||
/// <summary>
|
||||
@ -77,15 +67,6 @@ namespace osu.Game.Rulesets.UI
|
||||
Ruleset = ruleset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether all HitObjects have been judged, and invokes OnAllJudged.
|
||||
/// </summary>
|
||||
protected void CheckAllJudged()
|
||||
{
|
||||
if (AllObjectsJudged)
|
||||
OnAllJudged?.Invoke();
|
||||
}
|
||||
|
||||
public abstract ScoreProcessor CreateScoreProcessor();
|
||||
|
||||
/// <summary>
|
||||
@ -152,7 +133,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
|
||||
|
||||
protected override bool AllObjectsJudged => drawableObjects.All(h => h.AllJudged);
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
|
||||
|
||||
/// <summary>
|
||||
/// The playfield.
|
||||
@ -162,8 +143,6 @@ namespace osu.Game.Rulesets.UI
|
||||
protected override Container<Drawable> Content => content;
|
||||
private Container content;
|
||||
|
||||
private readonly List<DrawableHitObject<TObject>> drawableObjects = new List<DrawableHitObject<TObject>>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset.
|
||||
/// Creates a hit renderer for a beatmap.
|
||||
@ -250,8 +229,6 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
private void loadObjects()
|
||||
{
|
||||
drawableObjects.Capacity = Beatmap.HitObjects.Count;
|
||||
|
||||
foreach (TObject h in Beatmap.HitObjects)
|
||||
{
|
||||
var drawableObject = GetVisualRepresentation(h);
|
||||
@ -263,10 +240,8 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
Playfield.OnJudgement(d, j);
|
||||
OnJudgement?.Invoke(j);
|
||||
CheckAllJudged();
|
||||
};
|
||||
|
||||
drawableObjects.Add(drawableObject);
|
||||
Playfield.Add(drawableObject);
|
||||
}
|
||||
|
||||
|
@ -201,10 +201,10 @@ namespace osu.Game.Screens.Menu
|
||||
var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y);
|
||||
|
||||
var rectangle = new Quad(
|
||||
(barPosition - bottomOffset) * DrawInfo.Matrix,
|
||||
(barPosition - bottomOffset + amplitudeOffset) * DrawInfo.Matrix,
|
||||
(barPosition + bottomOffset) * DrawInfo.Matrix,
|
||||
(barPosition + bottomOffset + amplitudeOffset) * DrawInfo.Matrix
|
||||
Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix)
|
||||
);
|
||||
|
||||
Texture.DrawQuad(
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Screens.Menu
|
||||
OnEdit = delegate { Push(new Editor()); },
|
||||
OnSolo = delegate { Push(consumeSongSelect()); },
|
||||
OnMulti = delegate { Push(new Lobby()); },
|
||||
OnExit = delegate { Exit(); },
|
||||
OnExit = Exit,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -206,10 +206,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
hudOverlay.ModDisplay.Current.BindTo(working.Mods);
|
||||
|
||||
//bind RulesetContainer to ScoreProcessor and ourselves (for a pass situation)
|
||||
RulesetContainer.OnAllJudged += onCompletion;
|
||||
|
||||
//bind ScoreProcessor to ourselves (for a fail situation)
|
||||
// Bind ScoreProcessor to ourselves
|
||||
scoreProcessor.AllJudged += onCompletion;
|
||||
scoreProcessor.Failed += onFail;
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
@ -34,10 +35,12 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
foreach (var h in objects)
|
||||
{
|
||||
IHasEndTime end = h as IHasEndTime;
|
||||
var endTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
|
||||
|
||||
Debug.Assert(endTime >= h.StartTime);
|
||||
|
||||
int startRange = (int)((h.StartTime - firstHit) / interval);
|
||||
int endRange = (int)(((end?.EndTime > 0 ? end.EndTime : h.StartTime) - firstHit) / interval);
|
||||
int endRange = (int)((endTime - firstHit) / interval);
|
||||
for (int i = startRange; i <= endRange; i++)
|
||||
Values[i]++;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ namespace osu.Game.Screens.Select
|
||||
Origin = Anchor.CentreRight,
|
||||
SelectionChanged = carouselSelectionChanged,
|
||||
BeatmapsChanged = carouselBeatmapsLoaded,
|
||||
DeleteRequested = b => promptDelete(b),
|
||||
DeleteRequested = promptDelete,
|
||||
RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); },
|
||||
HideDifficultyRequested = b => manager.Hide(b),
|
||||
StartRequested = () => carouselRaisedStart(),
|
||||
|
@ -237,7 +237,7 @@ namespace osu.Game.Screens.Tournament
|
||||
RelativeSizeAxes = Axes.X,
|
||||
|
||||
Text = "Reset",
|
||||
Action = () => reset(false)
|
||||
Action = () => reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
35
osu.Game/Storyboards/CommandLoop.cs
Normal file
35
osu.Game/Storyboards/CommandLoop.cs
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class CommandLoop : CommandTimelineGroup
|
||||
{
|
||||
public double LoopStartTime;
|
||||
public int LoopCount;
|
||||
|
||||
public override double StartTime => LoopStartTime;
|
||||
public override double EndTime => LoopStartTime + CommandsDuration * LoopCount;
|
||||
|
||||
public CommandLoop(double startTime, int loopCount)
|
||||
{
|
||||
LoopStartTime = startTime;
|
||||
LoopCount = loopCount;
|
||||
}
|
||||
|
||||
public override IEnumerable<CommandTimeline<T>.TypedCommand> GetCommands<T>(CommandTimelineSelector<T> timelineSelector, double offset = 0)
|
||||
{
|
||||
for (var loop = 0; loop < LoopCount; loop++)
|
||||
{
|
||||
var loopOffset = LoopStartTime + loop * CommandsDuration;
|
||||
foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset))
|
||||
yield return command;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{LoopStartTime} x{LoopCount}";
|
||||
}
|
||||
}
|
77
osu.Game/Storyboards/CommandTimeline.cs
Normal file
77
osu.Game/Storyboards/CommandTimeline.cs
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class CommandTimeline<T> : ICommandTimeline
|
||||
{
|
||||
private readonly List<TypedCommand> commands = new List<TypedCommand>();
|
||||
public IEnumerable<TypedCommand> Commands => commands.OrderBy(c => c.StartTime);
|
||||
public bool HasCommands => commands.Count > 0;
|
||||
|
||||
private Cached<double> startTimeBacking;
|
||||
public double StartTime => startTimeBacking.IsValid ? startTimeBacking : (startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue);
|
||||
|
||||
private Cached<double> endTimeBacking;
|
||||
public double EndTime => endTimeBacking.IsValid ? endTimeBacking : (endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue);
|
||||
|
||||
public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default(T);
|
||||
public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default(T);
|
||||
|
||||
public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue)
|
||||
{
|
||||
if (endTime < startTime)
|
||||
return;
|
||||
|
||||
commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue, });
|
||||
|
||||
startTimeBacking.Invalidate();
|
||||
endTimeBacking.Invalidate();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{commands.Count} command(s)";
|
||||
|
||||
public class TypedCommand : ICommand
|
||||
{
|
||||
public Easing Easing { get; set; }
|
||||
public double StartTime { get; set; }
|
||||
public double EndTime { get; set; }
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public T StartValue;
|
||||
public T EndValue;
|
||||
|
||||
public int CompareTo(ICommand other)
|
||||
{
|
||||
var result = StartTime.CompareTo(other.StartTime);
|
||||
if (result != 0) return result;
|
||||
return EndTime.CompareTo(other.EndTime);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{StartTime} -> {EndTime}, {StartValue} -> {EndValue} {Easing}";
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICommandTimeline
|
||||
{
|
||||
double StartTime { get; }
|
||||
double EndTime { get; }
|
||||
bool HasCommands { get; }
|
||||
}
|
||||
|
||||
public interface ICommand : IComparable<ICommand>
|
||||
{
|
||||
Easing Easing { get; set; }
|
||||
double StartTime { get; set; }
|
||||
double EndTime { get; set; }
|
||||
double Duration { get; }
|
||||
}
|
||||
}
|
68
osu.Game/Storyboards/CommandTimelineGroup.cs
Normal file
68
osu.Game/Storyboards/CommandTimelineGroup.cs
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public delegate CommandTimeline<T> CommandTimelineSelector<T>(CommandTimelineGroup commandTimelineGroup);
|
||||
|
||||
public class CommandTimelineGroup
|
||||
{
|
||||
public CommandTimeline<float> X = new CommandTimeline<float>();
|
||||
public CommandTimeline<float> Y = new CommandTimeline<float>();
|
||||
public CommandTimeline<Vector2> Scale = new CommandTimeline<Vector2>();
|
||||
public CommandTimeline<float> Rotation = new CommandTimeline<float>();
|
||||
public CommandTimeline<Color4> Colour = new CommandTimeline<Color4>();
|
||||
public CommandTimeline<float> Alpha = new CommandTimeline<float>();
|
||||
public CommandTimeline<BlendingMode> BlendingMode = new CommandTimeline<BlendingMode>();
|
||||
public CommandTimeline<bool> FlipH = new CommandTimeline<bool>();
|
||||
public CommandTimeline<bool> FlipV = new CommandTimeline<bool>();
|
||||
|
||||
public IEnumerable<ICommandTimeline> Timelines
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return X;
|
||||
yield return Y;
|
||||
yield return Scale;
|
||||
yield return Rotation;
|
||||
yield return Colour;
|
||||
yield return Alpha;
|
||||
yield return BlendingMode;
|
||||
yield return FlipH;
|
||||
yield return FlipV;
|
||||
}
|
||||
}
|
||||
|
||||
public double CommandsStartTime => Timelines.Where(t => t.HasCommands).Min(t => t.StartTime);
|
||||
public double CommandsEndTime => Timelines.Where(t => t.HasCommands).Max(t => t.EndTime);
|
||||
public double CommandsDuration => CommandsEndTime - CommandsStartTime;
|
||||
|
||||
public virtual double StartTime => CommandsStartTime;
|
||||
public virtual double EndTime => CommandsEndTime;
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public bool HasCommands => Timelines.Any(t => t.HasCommands);
|
||||
|
||||
public virtual IEnumerable<CommandTimeline<T>.TypedCommand> GetCommands<T>(CommandTimelineSelector<T> timelineSelector, double offset = 0)
|
||||
{
|
||||
if (offset != 0)
|
||||
return timelineSelector(this).Commands.Select(command =>
|
||||
new CommandTimeline<T>.TypedCommand
|
||||
{
|
||||
Easing = command.Easing,
|
||||
StartTime = offset + command.StartTime,
|
||||
EndTime = offset + command.EndTime,
|
||||
StartValue = command.StartValue,
|
||||
EndValue = command.EndValue,
|
||||
});
|
||||
|
||||
return timelineSelector(this).Commands;
|
||||
}
|
||||
}
|
||||
}
|
24
osu.Game/Storyboards/CommandTrigger.cs
Normal file
24
osu.Game/Storyboards/CommandTrigger.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class CommandTrigger : CommandTimelineGroup
|
||||
{
|
||||
public string TriggerName;
|
||||
public double TriggerStartTime;
|
||||
public double TriggerEndTime;
|
||||
public int GroupNumber;
|
||||
|
||||
public CommandTrigger(string triggerName, double startTime, double endTime, int groupNumber)
|
||||
{
|
||||
TriggerName = triggerName;
|
||||
TriggerStartTime = startTime;
|
||||
TriggerEndTime = endTime;
|
||||
GroupNumber = groupNumber;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{TriggerName} {TriggerStartTime} -> {TriggerEndTime} ({GroupNumber})";
|
||||
}
|
||||
}
|
59
osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
Normal file
59
osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
Normal file
@ -0,0 +1,59 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.IO;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
public class DrawableStoryboard : Container<DrawableStoryboardLayer>
|
||||
{
|
||||
public Storyboard Storyboard { get; private set; }
|
||||
|
||||
protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480);
|
||||
public override bool HandleInput => false;
|
||||
|
||||
private bool passing = true;
|
||||
public bool Passing
|
||||
{
|
||||
get { return passing; }
|
||||
set
|
||||
{
|
||||
if (passing == value) return;
|
||||
passing = value;
|
||||
updateLayerVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
|
||||
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
|
||||
public DrawableStoryboard(Storyboard storyboard)
|
||||
{
|
||||
Storyboard = storyboard;
|
||||
Size = new Vector2(640, 480);
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(FileStore fileStore)
|
||||
{
|
||||
dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false) { ScaleAdjust = 1, });
|
||||
|
||||
foreach (var layer in Storyboard.Layers)
|
||||
Add(layer.CreateDrawable());
|
||||
}
|
||||
|
||||
private void updateLayerVisibility()
|
||||
{
|
||||
foreach (var layer in Children)
|
||||
layer.Enabled = passing ? layer.Layer.EnabledWhenPassing : layer.Layer.EnabledWhenFailing;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Animations;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
public class DrawableStoryboardAnimation : TextureAnimation, IFlippable
|
||||
{
|
||||
public StoryboardAnimation Animation { get; private set; }
|
||||
|
||||
protected override bool ShouldBeAlive => Animation.HasCommands && base.ShouldBeAlive;
|
||||
public override bool RemoveWhenNotAlive => !Animation.HasCommands || base.RemoveWhenNotAlive;
|
||||
|
||||
public bool FlipH { get; set; }
|
||||
public bool FlipV { get; set; }
|
||||
|
||||
protected override Vector2 DrawScale
|
||||
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);
|
||||
|
||||
public override Anchor Origin
|
||||
{
|
||||
get
|
||||
{
|
||||
var origin = base.Origin;
|
||||
|
||||
if (FlipH)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.x0))
|
||||
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
else if (origin.HasFlag(Anchor.x2))
|
||||
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
}
|
||||
|
||||
if (FlipV)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.y0))
|
||||
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
else if (origin.HasFlag(Anchor.y2))
|
||||
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsPresent
|
||||
=> !float.IsNaN(DrawPosition.X) && !float.IsNaN(DrawPosition.Y) && base.IsPresent;
|
||||
|
||||
public DrawableStoryboardAnimation(StoryboardAnimation animation)
|
||||
{
|
||||
Animation = animation;
|
||||
Origin = animation.Origin;
|
||||
Position = animation.InitialPosition;
|
||||
Repeat = animation.LoopType == AnimationLoopType.LoopForever;
|
||||
|
||||
if (animation.HasCommands)
|
||||
{
|
||||
LifetimeStart = animation.StartTime;
|
||||
LifetimeEnd = animation.EndTime;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, TextureStore textureStore)
|
||||
{
|
||||
var basePath = Animation.Path.ToLowerInvariant();
|
||||
for (var frame = 0; frame < Animation.FrameCount; frame++)
|
||||
{
|
||||
var framePath = basePath.Replace(".", frame + ".");
|
||||
|
||||
var path = game.Beatmap.Value.BeatmapSetInfo.Files.FirstOrDefault(f => f.Filename.ToLowerInvariant() == framePath)?.FileInfo.StoragePath;
|
||||
if (path == null)
|
||||
continue;
|
||||
|
||||
var texture = textureStore.Get(path);
|
||||
AddFrame(texture, Animation.FrameDelay);
|
||||
}
|
||||
Animation.ApplyTransforms(this);
|
||||
}
|
||||
}
|
||||
}
|
37
osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
Normal file
37
osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
public class DrawableStoryboardLayer : Container
|
||||
{
|
||||
public StoryboardLayer Layer { get; private set; }
|
||||
public bool Enabled;
|
||||
|
||||
public override bool IsPresent => Enabled && base.IsPresent;
|
||||
|
||||
public DrawableStoryboardLayer(StoryboardLayer layer)
|
||||
{
|
||||
Layer = layer;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
Enabled = layer.EnabledWhenPassing;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
foreach (var element in Layer.Elements)
|
||||
{
|
||||
var drawable = element.CreateDrawable();
|
||||
if (drawable != null)
|
||||
Add(drawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
80
osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs
Normal file
80
osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs
Normal file
@ -0,0 +1,80 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
public class DrawableStoryboardSprite : Sprite, IFlippable
|
||||
{
|
||||
public StoryboardSprite Sprite { get; private set; }
|
||||
|
||||
protected override bool ShouldBeAlive => Sprite.HasCommands && base.ShouldBeAlive;
|
||||
public override bool RemoveWhenNotAlive => !Sprite.HasCommands || base.RemoveWhenNotAlive;
|
||||
|
||||
public bool FlipH { get; set; }
|
||||
public bool FlipV { get; set; }
|
||||
|
||||
protected override Vector2 DrawScale
|
||||
=> new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);
|
||||
|
||||
public override Anchor Origin
|
||||
{
|
||||
get
|
||||
{
|
||||
var origin = base.Origin;
|
||||
|
||||
if (FlipH)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.x0))
|
||||
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
else if (origin.HasFlag(Anchor.x2))
|
||||
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
|
||||
}
|
||||
|
||||
if (FlipV)
|
||||
{
|
||||
if (origin.HasFlag(Anchor.y0))
|
||||
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
else if (origin.HasFlag(Anchor.y2))
|
||||
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsPresent
|
||||
=> !float.IsNaN(DrawPosition.X) && !float.IsNaN(DrawPosition.Y) && base.IsPresent;
|
||||
|
||||
public DrawableStoryboardSprite(StoryboardSprite sprite)
|
||||
{
|
||||
Sprite = sprite;
|
||||
Origin = sprite.Origin;
|
||||
Position = sprite.InitialPosition;
|
||||
|
||||
if (sprite.HasCommands)
|
||||
{
|
||||
LifetimeStart = sprite.StartTime;
|
||||
LifetimeEnd = sprite.EndTime;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, TextureStore textureStore)
|
||||
{
|
||||
var spritePath = Sprite.Path.ToLowerInvariant();
|
||||
var path = game.Beatmap.Value.BeatmapSetInfo.Files.FirstOrDefault(f => f.Filename.ToLowerInvariant() == spritePath)?.FileInfo.StoragePath;
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
Texture = textureStore.Get(path);
|
||||
Sprite.ApplyTransforms(this);
|
||||
}
|
||||
}
|
||||
}
|
30
osu.Game/Storyboards/Drawables/DrawablesExtensions.cs
Normal file
30
osu.Game/Storyboards/Drawables/DrawablesExtensions.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
public static class DrawablesExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adjusts <see cref="Drawable.Blending"/> after a delay.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||
public static TransformSequence<T> TransformBlendingMode<T>(this T drawable, BlendingMode newValue, double delay = 0)
|
||||
where T : Drawable
|
||||
=> drawable.TransformTo(drawable.PopulateTransform(new TransformBlendingMode(), newValue, delay));
|
||||
}
|
||||
|
||||
public class TransformBlendingMode : Transform<BlendingMode, Drawable>
|
||||
{
|
||||
private BlendingMode valueAt(double time)
|
||||
=> time < EndTime ? StartValue : EndValue;
|
||||
|
||||
public override string TargetMember => nameof(Drawable.Blending);
|
||||
|
||||
protected override void Apply(Drawable d, double time) => d.Blending = valueAt(time);
|
||||
protected override void ReadIntoStartValue(Drawable d) => StartValue = d.Blending.Mode;
|
||||
}
|
||||
}
|
55
osu.Game/Storyboards/Drawables/IFlippable.cs
Normal file
55
osu.Game/Storyboards/Drawables/IFlippable.cs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
public interface IFlippable : ITransformable
|
||||
{
|
||||
bool FlipH { get; set; }
|
||||
bool FlipV { get; set; }
|
||||
}
|
||||
|
||||
public class TransformFlipH : Transform<bool, IFlippable>
|
||||
{
|
||||
private bool valueAt(double time)
|
||||
=> time < EndTime ? StartValue : EndValue;
|
||||
|
||||
public override string TargetMember => nameof(IFlippable.FlipH);
|
||||
|
||||
protected override void Apply(IFlippable d, double time) => d.FlipH = valueAt(time);
|
||||
protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipH;
|
||||
}
|
||||
|
||||
public class TransformFlipV : Transform<bool, IFlippable>
|
||||
{
|
||||
private bool valueAt(double time)
|
||||
=> time < EndTime ? StartValue : EndValue;
|
||||
|
||||
public override string TargetMember => nameof(IFlippable.FlipV);
|
||||
|
||||
protected override void Apply(IFlippable d, double time) => d.FlipV = valueAt(time);
|
||||
protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipV;
|
||||
}
|
||||
|
||||
public static class FlippableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adjusts <see cref="IFlippable.FlipH"/> after a delay.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||
public static TransformSequence<T> TransformFlipH<T>(this T flippable, bool newValue, double delay = 0)
|
||||
where T : IFlippable
|
||||
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipH(), newValue, delay));
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts <see cref="IFlippable.FlipV"/> after a delay.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||
public static TransformSequence<T> TransformFlipV<T>(this T flippable, bool newValue, double delay = 0)
|
||||
where T : IFlippable
|
||||
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipV(), newValue, delay));
|
||||
}
|
||||
}
|
13
osu.Game/Storyboards/IStoryboardElement.cs
Normal file
13
osu.Game/Storyboards/IStoryboardElement.cs
Normal file
@ -0,0 +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 osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public interface IStoryboardElement
|
||||
{
|
||||
string Path { get; }
|
||||
Drawable CreateDrawable();
|
||||
}
|
||||
}
|
35
osu.Game/Storyboards/Storyboard.cs
Normal file
35
osu.Game/Storyboards/Storyboard.cs
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class Storyboard
|
||||
{
|
||||
private readonly Dictionary<string, StoryboardLayer> layers = new Dictionary<string, StoryboardLayer>();
|
||||
public IEnumerable<StoryboardLayer> Layers => layers.Values;
|
||||
|
||||
public Storyboard()
|
||||
{
|
||||
layers.Add("Background", new StoryboardLayer("Background", 3));
|
||||
layers.Add("Fail", new StoryboardLayer("Fail", 2) { EnabledWhenPassing = false, });
|
||||
layers.Add("Pass", new StoryboardLayer("Pass", 1) { EnabledWhenFailing = false, });
|
||||
layers.Add("Foreground", new StoryboardLayer("Foreground", 0));
|
||||
}
|
||||
|
||||
public StoryboardLayer GetLayer(string name)
|
||||
{
|
||||
StoryboardLayer layer;
|
||||
if (!layers.TryGetValue(name, out layer))
|
||||
layers[name] = layer = new StoryboardLayer(name, layers.Values.Min(l => l.Depth) - 1);
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
public DrawableStoryboard CreateDrawable()
|
||||
=> new DrawableStoryboard(this);
|
||||
}
|
||||
}
|
33
osu.Game/Storyboards/StoryboardAnimation.cs
Normal file
33
osu.Game/Storyboards/StoryboardAnimation.cs
Normal 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 OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class StoryboardAnimation : StoryboardSprite
|
||||
{
|
||||
public int FrameCount;
|
||||
public double FrameDelay;
|
||||
public AnimationLoopType LoopType;
|
||||
|
||||
public StoryboardAnimation(string path, Anchor origin, Vector2 initialPosition, int frameCount, double frameDelay, AnimationLoopType loopType)
|
||||
: base(path, origin, initialPosition)
|
||||
{
|
||||
FrameCount = frameCount;
|
||||
FrameDelay = frameDelay;
|
||||
LoopType = loopType;
|
||||
}
|
||||
|
||||
public override Drawable CreateDrawable()
|
||||
=> new DrawableStoryboardAnimation(this);
|
||||
}
|
||||
|
||||
public enum AnimationLoopType
|
||||
{
|
||||
LoopForever,
|
||||
LoopOnce,
|
||||
}
|
||||
}
|
33
osu.Game/Storyboards/StoryboardLayer.cs
Normal file
33
osu.Game/Storyboards/StoryboardLayer.cs
Normal 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 osu.Game.Storyboards.Drawables;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class StoryboardLayer
|
||||
{
|
||||
public string Name;
|
||||
public int Depth;
|
||||
public bool EnabledWhenPassing = true;
|
||||
public bool EnabledWhenFailing = true;
|
||||
|
||||
private readonly List<IStoryboardElement> elements = new List<IStoryboardElement>();
|
||||
public IEnumerable<IStoryboardElement> Elements => elements;
|
||||
|
||||
public StoryboardLayer(string name, int depth)
|
||||
{
|
||||
Name = name;
|
||||
Depth = depth;
|
||||
}
|
||||
|
||||
public void Add(IStoryboardElement element)
|
||||
{
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
public DrawableStoryboardLayer CreateDrawable()
|
||||
=> new DrawableStoryboardLayer(this) { Depth = Depth, };
|
||||
}
|
||||
}
|
24
osu.Game/Storyboards/StoryboardSample.cs
Normal file
24
osu.Game/Storyboards/StoryboardSample.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class StoryboardSample : IStoryboardElement
|
||||
{
|
||||
public string Path { get; set; }
|
||||
public double Time;
|
||||
public float Volume;
|
||||
|
||||
public StoryboardSample(string path, double time, float volume)
|
||||
{
|
||||
Path = path;
|
||||
Time = time;
|
||||
Volume = volume;
|
||||
}
|
||||
|
||||
public Drawable CreateDrawable()
|
||||
=> null;
|
||||
}
|
||||
}
|
113
osu.Game/Storyboards/StoryboardSprite.cs
Normal file
113
osu.Game/Storyboards/StoryboardSprite.cs
Normal file
@ -0,0 +1,113 @@
|
||||
// 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.Game.Storyboards.Drawables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Storyboards
|
||||
{
|
||||
public class StoryboardSprite : IStoryboardElement
|
||||
{
|
||||
private readonly List<CommandLoop> loops = new List<CommandLoop>();
|
||||
private readonly List<CommandTrigger> triggers = new List<CommandTrigger>();
|
||||
|
||||
public string Path { get; set; }
|
||||
public Anchor Origin;
|
||||
public Vector2 InitialPosition;
|
||||
|
||||
public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup();
|
||||
|
||||
public double StartTime => Math.Min(
|
||||
TimelineGroup.HasCommands ? TimelineGroup.CommandsStartTime : double.MaxValue,
|
||||
loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Min(l => l.StartTime) : double.MaxValue);
|
||||
|
||||
public double EndTime => Math.Max(
|
||||
TimelineGroup.HasCommands ? TimelineGroup.CommandsEndTime : double.MinValue,
|
||||
loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Max(l => l.EndTime) : double.MinValue);
|
||||
|
||||
public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands);
|
||||
|
||||
private delegate void DrawablePropertyInitializer<in T>(Drawable drawable, T value);
|
||||
private delegate void DrawableTransformer<in T>(Drawable drawable, T value, double duration, Easing easing);
|
||||
|
||||
public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition)
|
||||
{
|
||||
Path = path;
|
||||
Origin = origin;
|
||||
InitialPosition = initialPosition;
|
||||
}
|
||||
|
||||
public CommandLoop AddLoop(double startTime, int loopCount)
|
||||
{
|
||||
var loop = new CommandLoop(startTime, loopCount);
|
||||
loops.Add(loop);
|
||||
return loop;
|
||||
}
|
||||
|
||||
public CommandTrigger AddTrigger(string triggerName, double startTime, double endTime, int groupNumber)
|
||||
{
|
||||
var trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber);
|
||||
triggers.Add(trigger);
|
||||
return trigger;
|
||||
}
|
||||
|
||||
public virtual Drawable CreateDrawable()
|
||||
=> new DrawableStoryboardSprite(this);
|
||||
|
||||
public void ApplyTransforms(Drawable drawable, IEnumerable<Tuple<CommandTimelineGroup, double>> triggeredGroups = null)
|
||||
{
|
||||
applyCommands(drawable, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value, (d, value, duration, easing) => d.MoveToX(value, duration, easing));
|
||||
applyCommands(drawable, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value, (d, value, duration, easing) => d.MoveToY(value, duration, easing));
|
||||
applyCommands(drawable, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value, (d, value, duration, easing) => d.ScaleTo(value, duration, easing));
|
||||
applyCommands(drawable, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing));
|
||||
applyCommands(drawable, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing));
|
||||
applyCommands(drawable, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing));
|
||||
applyCommands(drawable, getCommands(g => g.BlendingMode, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false);
|
||||
|
||||
var flippable = drawable as IFlippable;
|
||||
if (flippable != null)
|
||||
{
|
||||
applyCommands(drawable, getCommands(g => g.FlipH, triggeredGroups), (d, value) => flippable.FlipH = value, (d, value, duration, easing) => flippable.TransformFlipH(value, duration), false);
|
||||
applyCommands(drawable, getCommands(g => g.FlipV, triggeredGroups), (d, value) => flippable.FlipV = value, (d, value, duration, easing) => flippable.TransformFlipV(value, duration), false);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyCommands<T>(Drawable drawable, IEnumerable<CommandTimeline<T>.TypedCommand> commands, DrawablePropertyInitializer<T> initializeProperty, DrawableTransformer<T> transform, bool alwaysInitialize = true)
|
||||
where T : struct
|
||||
{
|
||||
var initialized = false;
|
||||
foreach (var command in commands.OrderBy(l => l))
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
if (alwaysInitialize || command.StartTime == command.EndTime)
|
||||
initializeProperty.Invoke(drawable, command.StartValue);
|
||||
initialized = true;
|
||||
}
|
||||
using (drawable.BeginAbsoluteSequence(command.StartTime))
|
||||
{
|
||||
transform(drawable, command.StartValue, 0, Easing.None);
|
||||
transform(drawable, command.EndValue, command.Duration, command.Easing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<CommandTimeline<T>.TypedCommand> getCommands<T>(CommandTimelineSelector<T> timelineSelector, IEnumerable<Tuple<CommandTimelineGroup, double>> triggeredGroups)
|
||||
{
|
||||
var commands = TimelineGroup.GetCommands(timelineSelector);
|
||||
foreach (var loop in loops)
|
||||
commands = commands.Concat(loop.GetCommands(timelineSelector));
|
||||
if (triggeredGroups != null)
|
||||
foreach (var pair in triggeredGroups)
|
||||
commands = commands.Concat(pair.Item1.GetCommands(timelineSelector, pair.Item2));
|
||||
return commands;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> $"{Path}, {Origin}, {InitialPosition}";
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ namespace osu.Game.Users
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
Texture texture = null;
|
||||
if (user?.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}");
|
||||
if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}");
|
||||
if (texture == null) texture = textures.Get(@"Online/avatar-guest");
|
||||
|
||||
Add(new Sprite
|
||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Users
|
||||
public int MaxCombo;
|
||||
|
||||
[JsonProperty(@"replays_watched_by_others")]
|
||||
public int ReplayWatched;
|
||||
public int ReplaysWatched;
|
||||
|
||||
[JsonProperty(@"grade_counts")]
|
||||
public Grades GradesCount;
|
||||
|
11
osu.Game/app.config
Normal file
11
osu.Game/app.config
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
@ -37,15 +37,16 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.17.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<HintPath>$(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||
@ -80,6 +81,22 @@
|
||||
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
|
||||
<Compile Include="Beatmaps\DummyWorkingBeatmap.cs" />
|
||||
<Compile Include="Beatmaps\IO\LegacyFilesystemReader.cs" />
|
||||
<Compile Include="Storyboards\Drawables\IFlippable.cs" />
|
||||
<Compile Include="Storyboards\Drawables\DrawableStoryboardLayer.cs" />
|
||||
<Compile Include="Storyboards\Drawables\DrawableStoryboard.cs" />
|
||||
<Compile Include="Storyboards\Drawables\DrawableStoryboardAnimation.cs" />
|
||||
<Compile Include="Storyboards\Drawables\DrawableStoryboardSprite.cs" />
|
||||
<Compile Include="Storyboards\StoryboardAnimation.cs" />
|
||||
<Compile Include="Storyboards\Drawables\DrawablesExtensions.cs" />
|
||||
<Compile Include="Storyboards\IStoryboardElement.cs" />
|
||||
<Compile Include="Storyboards\CommandTimeline.cs" />
|
||||
<Compile Include="Storyboards\CommandTimelineGroup.cs" />
|
||||
<Compile Include="Storyboards\CommandTrigger.cs" />
|
||||
<Compile Include="Storyboards\CommandLoop.cs" />
|
||||
<Compile Include="Storyboards\StoryboardSample.cs" />
|
||||
<Compile Include="Storyboards\StoryboardSprite.cs" />
|
||||
<Compile Include="Storyboards\StoryboardLayer.cs" />
|
||||
<Compile Include="Storyboards\Storyboard.cs" />
|
||||
<Compile Include="Database\StoreVersion.cs" />
|
||||
<Compile Include="Graphics\Containers\OsuClickableContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\OsuFocusedOverlayContainer.cs" />
|
||||
@ -568,6 +585,8 @@
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="OpenTK.dll.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
@ -5,9 +5,9 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
-->
|
||||
<packages>
|
||||
<package id="DotNetZip" version="1.10.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
|
||||
<package id="SharpCompress" version="0.17.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
||||
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
|
||||
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
|
||||
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
|
||||
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />
|
||||
|
Reference in New Issue
Block a user