mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 15:16:38 +09:00
Merge branch 'master' into osu-direct
This commit is contained in:
@ -2,11 +2,11 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Events;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
public BeatmapInfo BeatmapInfo;
|
||||
public TimingInfo TimingInfo = new TimingInfo();
|
||||
public EventInfo EventInfo = new EventInfo();
|
||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||
public readonly List<Color4> ComboColors = new List<Color4>
|
||||
{
|
||||
new Color4(17, 136, 170, 255),
|
||||
@ -34,6 +34,11 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public List<T> HitObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Total amount of break time in the beatmap.
|
||||
/// </summary>
|
||||
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new beatmap.
|
||||
/// </summary>
|
||||
@ -42,7 +47,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo;
|
||||
TimingInfo = original?.TimingInfo ?? TimingInfo;
|
||||
EventInfo = original?.EventInfo ?? EventInfo;
|
||||
Breaks = original?.Breaks ?? Breaks;
|
||||
ComboColors = original?.ComboColors ?? ComboColors;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +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
|
||||
|
||||
namespace osu.Game.Beatmaps.Events
|
||||
{
|
||||
public class BackgroundEvent : Event
|
||||
{
|
||||
/// <summary>
|
||||
/// The file name.
|
||||
/// </summary>
|
||||
public string Filename;
|
||||
}
|
||||
}
|
@ -1,13 +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
|
||||
|
||||
namespace osu.Game.Beatmaps.Events
|
||||
{
|
||||
public abstract class Event
|
||||
{
|
||||
/// <summary>
|
||||
/// The event start time.
|
||||
/// </summary>
|
||||
public double StartTime;
|
||||
}
|
||||
}
|
@ -1,33 +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.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Beatmaps.Events
|
||||
{
|
||||
public class EventInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// All the background events.
|
||||
/// </summary>
|
||||
public readonly List<BackgroundEvent> Backgrounds = new List<BackgroundEvent>();
|
||||
|
||||
/// <summary>
|
||||
/// All the break events.
|
||||
/// </summary>
|
||||
public readonly List<BreakEvent> Breaks = new List<BreakEvent>();
|
||||
|
||||
/// <summary>
|
||||
/// Total duration of all breaks.
|
||||
/// </summary>
|
||||
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the active background at a time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to retrieve the background at.</param>
|
||||
/// <returns>The background.</returns>
|
||||
public BackgroundEvent BackgroundAt(double time) => Backgrounds.FirstOrDefault(b => b.StartTime <= time);
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Events;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
@ -217,18 +216,12 @@ namespace osu.Game.Beatmaps.Formats
|
||||
case EventType.Background:
|
||||
string filename = split[2].Trim('"');
|
||||
|
||||
beatmap.EventInfo.Backgrounds.Add(new BackgroundEvent
|
||||
{
|
||||
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
|
||||
Filename = filename
|
||||
});
|
||||
|
||||
if (type == EventType.Background)
|
||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
|
||||
|
||||
break;
|
||||
case EventType.Break:
|
||||
var breakEvent = new BreakEvent
|
||||
var breakEvent = new BreakPeriod
|
||||
{
|
||||
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
|
||||
EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
|
||||
@ -237,7 +230,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (!breakEvent.HasEffect)
|
||||
return;
|
||||
|
||||
beatmap.EventInfo.Breaks.Add(breakEvent);
|
||||
beatmap.Breaks.Add(breakEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,32 @@
|
||||
// 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.Beatmaps.Events
|
||||
namespace osu.Game.Beatmaps.Timing
|
||||
{
|
||||
public class BreakEvent : Event
|
||||
public class BreakPeriod
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum duration required for a break to have any effect.
|
||||
/// </summary>
|
||||
private const double min_break_duration = 650;
|
||||
|
||||
/// <summary>
|
||||
/// The break start time.
|
||||
/// </summary>
|
||||
public double StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// The break end time.
|
||||
/// </summary>
|
||||
public double EndTime;
|
||||
|
||||
/// <summary>
|
||||
/// The duration of the break.
|
||||
/// The break duration.
|
||||
/// </summary>
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the break has any effect. Breaks that are too short are culled before they reach the EventInfo.
|
||||
/// Whether the break has any effect. Breaks that are too short are culled before they are added to the beatmap.
|
||||
/// </summary>
|
||||
public bool HasEffect => Duration >= min_break_duration;
|
||||
}
|
60
osu.Game/Graphics/Containers/BeatSyncedContainer.cs
Normal file
60
osu.Game/Graphics/Containers/BeatSyncedContainer.cs
Normal 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
public class BeatSyncedContainer : Container
|
||||
{
|
||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
private int lastBeat;
|
||||
private ControlPoint lastControlPoint;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (beatmap.Value?.Track == null)
|
||||
return;
|
||||
|
||||
double currentTrackTime = beatmap.Value.Track.CurrentTime;
|
||||
ControlPoint overridePoint;
|
||||
ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(currentTrackTime, out overridePoint);
|
||||
|
||||
if (controlPoint.BeatLength == 0)
|
||||
return;
|
||||
|
||||
bool kiai = (overridePoint ?? controlPoint).KiaiMode;
|
||||
int beat = (int)((currentTrackTime - controlPoint.Time) / controlPoint.BeatLength);
|
||||
|
||||
// The beats before the start of the first control point are off by 1, this should do the trick
|
||||
if (currentTrackTime < controlPoint.Time)
|
||||
beat--;
|
||||
|
||||
if (controlPoint == lastControlPoint && beat == lastBeat)
|
||||
return;
|
||||
|
||||
double offsetFromBeat = (controlPoint.Time - currentTrackTime) % controlPoint.BeatLength;
|
||||
|
||||
using (BeginDelayedSequence(offsetFromBeat, true))
|
||||
OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai);
|
||||
|
||||
lastBeat = beat;
|
||||
lastControlPoint = controlPoint;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game)
|
||||
{
|
||||
beatmap.BindTo(game.Beatmap);
|
||||
}
|
||||
|
||||
protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
171
osu.Game/Graphics/Containers/SectionsContainer.cs
Normal file
171
osu.Game/Graphics/Containers/SectionsContainer.cs
Normal file
@ -0,0 +1,171 @@
|
||||
// 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 osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that can scroll to each section inside it.
|
||||
/// </summary>
|
||||
public class SectionsContainer : Container
|
||||
{
|
||||
private Drawable expandableHeader, fixedHeader, footer;
|
||||
public readonly ScrollContainer ScrollContainer;
|
||||
private readonly Container<Drawable> sectionsContainer;
|
||||
|
||||
public Drawable ExpandableHeader
|
||||
{
|
||||
get { return expandableHeader; }
|
||||
set
|
||||
{
|
||||
if (value == expandableHeader) return;
|
||||
|
||||
if (expandableHeader != null)
|
||||
Remove(expandableHeader);
|
||||
expandableHeader = value;
|
||||
if (value == null) return;
|
||||
|
||||
Add(expandableHeader);
|
||||
lastKnownScroll = float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable FixedHeader
|
||||
{
|
||||
get { return fixedHeader; }
|
||||
set
|
||||
{
|
||||
if (value == fixedHeader) return;
|
||||
|
||||
if (fixedHeader != null)
|
||||
Remove(fixedHeader);
|
||||
fixedHeader = value;
|
||||
if (value == null) return;
|
||||
|
||||
Add(fixedHeader);
|
||||
lastKnownScroll = float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable Footer
|
||||
{
|
||||
get { return footer; }
|
||||
set
|
||||
{
|
||||
if (value == footer) return;
|
||||
|
||||
if (footer != null)
|
||||
ScrollContainer.Remove(footer);
|
||||
footer = value;
|
||||
if (value == null) return;
|
||||
|
||||
footer.Anchor |= Anchor.y2;
|
||||
footer.Origin |= Anchor.y2;
|
||||
ScrollContainer.Add(footer);
|
||||
lastKnownScroll = float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
public Bindable<Drawable> SelectedSection { get; } = new Bindable<Drawable>();
|
||||
|
||||
protected virtual Container<Drawable> CreateScrollContentContainer()
|
||||
=> new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both
|
||||
};
|
||||
|
||||
private List<Drawable> sections = new List<Drawable>();
|
||||
public IEnumerable<Drawable> Sections
|
||||
{
|
||||
get { return sections; }
|
||||
set
|
||||
{
|
||||
foreach (var section in sections)
|
||||
sectionsContainer.Remove(section);
|
||||
|
||||
sections = value.ToList();
|
||||
if (sections.Count == 0) return;
|
||||
|
||||
sectionsContainer.Add(sections);
|
||||
SelectedSection.Value = sections[0];
|
||||
lastKnownScroll = float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
private float headerHeight, footerHeight;
|
||||
private readonly MarginPadding originalSectionsMargin;
|
||||
private void updateSectionsMargin()
|
||||
{
|
||||
if (sections.Count == 0) return;
|
||||
|
||||
var newMargin = originalSectionsMargin;
|
||||
newMargin.Top += headerHeight;
|
||||
newMargin.Bottom += footerHeight;
|
||||
|
||||
sectionsContainer.Margin = newMargin;
|
||||
}
|
||||
|
||||
public SectionsContainer()
|
||||
{
|
||||
Add(ScrollContainer = new ScrollContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = false,
|
||||
Children = new Drawable[] { sectionsContainer = CreateScrollContentContainer() }
|
||||
});
|
||||
originalSectionsMargin = sectionsContainer.Margin;
|
||||
}
|
||||
|
||||
private float lastKnownScroll;
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
float headerH = (ExpandableHeader?.LayoutSize.Y ?? 0) + (FixedHeader?.LayoutSize.Y ?? 0);
|
||||
float footerH = Footer?.LayoutSize.Y ?? 0;
|
||||
if (headerH != headerHeight || footerH != footerHeight)
|
||||
{
|
||||
headerHeight = headerH;
|
||||
footerHeight = footerH;
|
||||
updateSectionsMargin();
|
||||
}
|
||||
|
||||
float currentScroll = Math.Max(0, ScrollContainer.Current);
|
||||
if (currentScroll != lastKnownScroll)
|
||||
{
|
||||
lastKnownScroll = currentScroll;
|
||||
|
||||
if (expandableHeader != null && fixedHeader != null)
|
||||
{
|
||||
float offset = Math.Min(expandableHeader.LayoutSize.Y, currentScroll);
|
||||
|
||||
expandableHeader.Y = -offset;
|
||||
fixedHeader.Y = -offset + expandableHeader.LayoutSize.Y;
|
||||
}
|
||||
|
||||
Drawable bestMatch = null;
|
||||
float minDiff = float.MaxValue;
|
||||
|
||||
foreach (var section in sections)
|
||||
{
|
||||
float diff = Math.Abs(ScrollContainer.GetChildPosInContent(section) - currentScroll);
|
||||
if (diff < minDiff)
|
||||
{
|
||||
minDiff = diff;
|
||||
bestMatch = section;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestMatch != null)
|
||||
SelectedSection.Value = bestMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -87,8 +87,5 @@ namespace osu.Game.Graphics
|
||||
public readonly Color4 RedDarker = FromHex(@"870000");
|
||||
|
||||
public readonly Color4 ChatBlue = FromHex(@"17292e");
|
||||
|
||||
public readonly Color4 SpinnerBase = FromHex(@"002c3c");
|
||||
public readonly Color4 SpinnerFill = FromHex(@"005b7c");
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ namespace osu.Game.Online.API
|
||||
{
|
||||
APIRequest req;
|
||||
while (oldQueue.TryDequeue(out req))
|
||||
req.Fail(new Exception(@"Disconnected from server"));
|
||||
req.Fail(new WebException(@"Disconnected from server"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Game.Online.Chat;
|
||||
|
||||
@ -20,10 +21,7 @@ namespace osu.Game.Online.API.Requests
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
string channelString = string.Empty;
|
||||
foreach (Channel c in channels)
|
||||
channelString += c.Id + ",";
|
||||
channelString = channelString.TrimEnd(',');
|
||||
string channelString = string.Join(",", channels.Select(x => x.Id));
|
||||
|
||||
var req = base.CreateWebRequest();
|
||||
req.AddParameter(@"channels", channelString);
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Online.Chat
|
||||
[JsonProperty(@"channel_id")]
|
||||
public int Id;
|
||||
|
||||
public readonly SortedList<Message> Messages = new SortedList<Message>((m1, m2) => m1.Id.CompareTo(m2.Id));
|
||||
public readonly SortedList<Message> Messages = new SortedList<Message>(Comparer<Message>.Default);
|
||||
|
||||
//internal bool Joined;
|
||||
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class Message
|
||||
public class Message : IComparable<Message>, IEquatable<Message>
|
||||
{
|
||||
[JsonProperty(@"message_id")]
|
||||
public readonly long Id;
|
||||
@ -42,17 +42,11 @@ namespace osu.Game.Online.Chat
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var objMessage = obj as Message;
|
||||
public int CompareTo(Message other) => Id.CompareTo(other.Id);
|
||||
|
||||
return Id == objMessage?.Id;
|
||||
}
|
||||
public bool Equals(Message other) => Id == other?.Id;
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id.GetHashCode();
|
||||
}
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
}
|
||||
|
||||
public enum TargetType
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Chat
|
||||
public class DrawableChannel : Container
|
||||
{
|
||||
public readonly Channel Channel;
|
||||
private readonly FillFlowContainer flow;
|
||||
private readonly FillFlowContainer<ChatLine> flow;
|
||||
private readonly ScrollContainer scroll;
|
||||
|
||||
public DrawableChannel(Channel channel)
|
||||
@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Chat
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
flow = new FillFlowContainer
|
||||
flow = new FillFlowContainer<ChatLine>
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
@ -63,19 +63,18 @@ namespace osu.Game.Overlays.Chat
|
||||
|
||||
var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MAX_HISTORY));
|
||||
|
||||
//up to last Channel.MAX_HISTORY messages
|
||||
flow.Add(displayMessages.Select(m => new ChatLine(m)));
|
||||
|
||||
if (scroll.IsScrolledToEnd(10) || !flow.Children.Any())
|
||||
scrollToEnd();
|
||||
|
||||
//up to last Channel.MAX_HISTORY messages
|
||||
foreach (Message m in displayMessages)
|
||||
{
|
||||
var d = new ChatLine(m);
|
||||
flow.Add(d);
|
||||
}
|
||||
var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
|
||||
int count = staleMessages.Length - Channel.MAX_HISTORY;
|
||||
|
||||
while (flow.Children.Count(c => c.LifetimeEnd == double.MaxValue) > Channel.MAX_HISTORY)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var d = flow.Children.First(c => c.LifetimeEnd == double.MaxValue);
|
||||
var d = staleMessages[i];
|
||||
if (!scroll.IsScrolledToEnd(10))
|
||||
scroll.OffsetScrollPosition(-d.DrawHeight);
|
||||
d.Expire();
|
||||
|
@ -229,6 +229,7 @@ namespace osu.Game.Overlays
|
||||
Scheduler.Add(delegate
|
||||
{
|
||||
loading.FadeOut(100);
|
||||
loading.Expire();
|
||||
|
||||
addChannel(channels.Find(c => c.Name == @"#lazer"));
|
||||
addChannel(channels.Find(c => c.Name == @"#osu"));
|
||||
@ -320,11 +321,8 @@ namespace osu.Game.Overlays
|
||||
fetchReq = new GetMessagesRequest(careChannels, lastMessageId);
|
||||
fetchReq.Success += delegate (List<Message> messages)
|
||||
{
|
||||
var ids = messages.Where(m => m.TargetType == TargetType.Channel).Select(m => m.TargetId).Distinct();
|
||||
|
||||
//batch messages per channel.
|
||||
foreach (var id in ids)
|
||||
careChannels.Find(c => c.Id == id)?.AddNewMessages(messages.Where(m => m.TargetId == id).ToArray());
|
||||
foreach (var group in messages.Where(m => m.TargetType == TargetType.Channel).GroupBy(m => m.TargetId))
|
||||
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
|
||||
|
||||
lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId;
|
||||
|
||||
|
@ -1,34 +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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
public class SettingsHeader : Container
|
||||
{
|
||||
public SearchTextBox SearchTextBox;
|
||||
|
||||
private Box background;
|
||||
|
||||
private readonly Func<float> currentScrollOffset;
|
||||
|
||||
public Action Exit;
|
||||
|
||||
/// <param name="currentScrollOffset">A reference to the current scroll position of the ScrollContainer we are contained within.</param>
|
||||
public SettingsHeader(Func<float> currentScrollOffset)
|
||||
{
|
||||
this.currentScrollOffset = currentScrollOffset;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
@ -37,11 +19,6 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
@ -53,7 +30,8 @@ namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
Text = "settings",
|
||||
TextSize = 40,
|
||||
Margin = new MarginPadding {
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Left = SettingsOverlay.CONTENT_MARGINS,
|
||||
Top = Toolbar.Toolbar.TOOLTIP_HEIGHT
|
||||
},
|
||||
@ -63,45 +41,15 @@ namespace osu.Game.Overlays.Settings
|
||||
Colour = colours.Pink,
|
||||
Text = "Change the way osu! behaves",
|
||||
TextSize = 18,
|
||||
Margin = new MarginPadding {
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Left = SettingsOverlay.CONTENT_MARGINS,
|
||||
Bottom = 30
|
||||
},
|
||||
},
|
||||
SearchTextBox = new SearchTextBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Width = 0.95f,
|
||||
Margin = new MarginPadding {
|
||||
Top = 20,
|
||||
Bottom = 20
|
||||
},
|
||||
Exit = () => Exit(),
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
// the point at which we will start anchoring to the top.
|
||||
float anchorOffset = SearchTextBox.Y;
|
||||
|
||||
float scrollPosition = currentScrollOffset();
|
||||
|
||||
// we want to anchor the search field to the top of the screen when scrolling.
|
||||
Margin = new MarginPadding { Top = Math.Max(0, scrollPosition - anchorOffset) };
|
||||
|
||||
// we don't want the header to scroll when scrolling beyond the upper extent.
|
||||
Y = Math.Min(0, scrollPosition);
|
||||
|
||||
// we get darker as scroll progresses
|
||||
background.Alpha = Math.Min(1, scrollPosition / anchorOffset) * 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Settings
|
||||
private readonly Box backgroundBox;
|
||||
private readonly Box selectionIndicator;
|
||||
private readonly Container text;
|
||||
public Action Action;
|
||||
public Action<SettingsSection> Action;
|
||||
|
||||
private SettingsSection section;
|
||||
public SettingsSection Section
|
||||
@ -75,6 +75,7 @@ namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
Width = Sidebar.DEFAULT_WIDTH,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Colour = OsuColour.Gray(0.6f),
|
||||
Children = new[]
|
||||
{
|
||||
headerText = new OsuSpriteText
|
||||
@ -110,7 +111,7 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
Action?.Invoke();
|
||||
Action?.Invoke(section);
|
||||
backgroundBox.FlashColour(Color4.White, 400);
|
||||
return true;
|
||||
}
|
||||
|
@ -7,10 +7,11 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using System;
|
||||
using osu.Game.Overlays.Settings.Sections;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.Settings.Sections;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
@ -26,18 +27,13 @@ namespace osu.Game.Overlays
|
||||
|
||||
private const float sidebar_padding = 10;
|
||||
|
||||
private ScrollContainer scrollContainer;
|
||||
private Sidebar sidebar;
|
||||
private SidebarButton[] sidebarButtons;
|
||||
private SettingsSection[] sections;
|
||||
private SidebarButton selectedSidebarButton;
|
||||
|
||||
private SettingsHeader header;
|
||||
private SettingsSectionsContainer sectionsContainer;
|
||||
|
||||
private SettingsFooter footer;
|
||||
|
||||
private SearchContainer searchContainer;
|
||||
|
||||
private float lastKnownScroll;
|
||||
private SearchTextBox searchTextBox;
|
||||
|
||||
public SettingsOverlay()
|
||||
{
|
||||
@ -48,7 +44,7 @@ namespace osu.Game.Overlays
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuGame game)
|
||||
{
|
||||
sections = new SettingsSection[]
|
||||
var sections = new SettingsSection[]
|
||||
{
|
||||
new GeneralSection(),
|
||||
new GraphicsSection(),
|
||||
@ -68,27 +64,27 @@ namespace osu.Game.Overlays
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f,
|
||||
},
|
||||
scrollContainer = new ScrollContainer
|
||||
sectionsContainer = new SettingsSectionsContainer
|
||||
{
|
||||
ScrollDraggerVisible = false,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = width,
|
||||
Margin = new MarginPadding { Left = SIDEBAR_WIDTH },
|
||||
Children = new Drawable[]
|
||||
ExpandableHeader = new SettingsHeader(),
|
||||
FixedHeader = searchTextBox = new SearchTextBox
|
||||
{
|
||||
searchContainer = new SearchContainer
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Width = 0.95f,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = sections,
|
||||
Top = 20,
|
||||
Bottom = 20
|
||||
},
|
||||
footer = new SettingsFooter(),
|
||||
header = new SettingsHeader(() => scrollContainer.Current)
|
||||
{
|
||||
Exit = Hide,
|
||||
},
|
||||
}
|
||||
Exit = Hide,
|
||||
},
|
||||
Sections = sections,
|
||||
Footer = new SettingsFooter()
|
||||
},
|
||||
sidebar = new Sidebar
|
||||
{
|
||||
@ -96,84 +92,89 @@ namespace osu.Game.Overlays
|
||||
Children = sidebarButtons = sections.Select(section =>
|
||||
new SidebarButton
|
||||
{
|
||||
Selected = sections[0] == section,
|
||||
Section = section,
|
||||
Action = () => scrollContainer.ScrollIntoView(section),
|
||||
Action = sectionsContainer.ScrollContainer.ScrollIntoView,
|
||||
}
|
||||
).ToArray()
|
||||
}
|
||||
};
|
||||
|
||||
header.SearchTextBox.Current.ValueChanged += newValue => searchContainer.SearchTerm = newValue;
|
||||
selectedSidebarButton = sidebarButtons[0];
|
||||
selectedSidebarButton.Selected = true;
|
||||
|
||||
scrollContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 };
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
//we need to update these manually because we can't put the SettingsHeader inside the SearchContainer (due to its anchoring).
|
||||
searchContainer.Y = header.DrawHeight;
|
||||
footer.Y = searchContainer.Y + searchContainer.DrawHeight;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
float currentScroll = scrollContainer.Current;
|
||||
if (currentScroll != lastKnownScroll)
|
||||
sectionsContainer.SelectedSection.ValueChanged += section =>
|
||||
{
|
||||
lastKnownScroll = currentScroll;
|
||||
selectedSidebarButton.Selected = false;
|
||||
selectedSidebarButton = sidebarButtons.Single(b => b.Section == section);
|
||||
selectedSidebarButton.Selected = true;
|
||||
};
|
||||
|
||||
SettingsSection bestCandidate = null;
|
||||
float bestDistance = float.MaxValue;
|
||||
searchTextBox.Current.ValueChanged += newValue => sectionsContainer.SearchContainer.SearchTerm = newValue;
|
||||
|
||||
foreach (SettingsSection section in sections)
|
||||
{
|
||||
float distance = Math.Abs(scrollContainer.GetChildPosInContent(section) - currentScroll);
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
bestDistance = distance;
|
||||
bestCandidate = section;
|
||||
}
|
||||
}
|
||||
|
||||
var previous = sidebarButtons.SingleOrDefault(sb => sb.Selected);
|
||||
var next = sidebarButtons.SingleOrDefault(sb => sb.Section == bestCandidate);
|
||||
if (previous != null) previous.Selected = false;
|
||||
if (next != null) next.Selected = true;
|
||||
}
|
||||
sectionsContainer.Padding = new MarginPadding { Top = game?.Toolbar.DrawHeight ?? 0 };
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
|
||||
scrollContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
sectionsContainer.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
sidebar.MoveToX(0, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
FadeTo(1, TRANSITION_LENGTH / 2);
|
||||
|
||||
header.SearchTextBox.HoldFocus = true;
|
||||
searchTextBox.HoldFocus = true;
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
scrollContainer.MoveToX(-width, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
sectionsContainer.MoveToX(-width, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
sidebar.MoveToX(-SIDEBAR_WIDTH, TRANSITION_LENGTH, EasingTypes.OutQuint);
|
||||
FadeTo(0, TRANSITION_LENGTH / 2);
|
||||
|
||||
header.SearchTextBox.HoldFocus = false;
|
||||
header.SearchTextBox.TriggerFocusLost();
|
||||
searchTextBox.HoldFocus = false;
|
||||
searchTextBox.TriggerFocusLost();
|
||||
}
|
||||
|
||||
protected override bool OnFocus(InputState state)
|
||||
{
|
||||
header.SearchTextBox.TriggerFocus(state);
|
||||
searchTextBox.TriggerFocus(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
private class SettingsSectionsContainer : SectionsContainer
|
||||
{
|
||||
public SearchContainer SearchContainer;
|
||||
private readonly Box headerBackground;
|
||||
|
||||
protected override Container<Drawable> CreateScrollContentContainer()
|
||||
=> SearchContainer = new SearchContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
};
|
||||
|
||||
public SettingsSectionsContainer()
|
||||
{
|
||||
ScrollContainer.ScrollDraggerVisible = false;
|
||||
Add(headerBackground = new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.X
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
// no null check because the usage of this class is strict
|
||||
headerBackground.Height = ExpandableHeader.LayoutSize.Y + FixedHeader.LayoutSize.Y;
|
||||
headerBackground.Y = ExpandableHeader.Y;
|
||||
headerBackground.Alpha = -ExpandableHeader.Y / ExpandableHeader.LayoutSize.Y * 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,12 +57,17 @@ namespace osu.Game.Screens
|
||||
beatmap.Value = localMap;
|
||||
}
|
||||
|
||||
beatmap.ValueChanged += OnBeatmapChanged;
|
||||
|
||||
if (osuGame != null)
|
||||
ruleset.BindTo(osuGame.Ruleset);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
beatmap.ValueChanged += OnBeatmapChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The global Beatmap was changed.
|
||||
/// </summary>
|
||||
|
@ -150,7 +150,7 @@ namespace osu.Game.Screens.Play
|
||||
FramedClock = offsetClock,
|
||||
OnRetry = Restart,
|
||||
OnQuit = Exit,
|
||||
CheckCanPause = () => ValidForResume && !HasFailed,
|
||||
CheckCanPause = () => ValidForResume && !HasFailed && !HitRenderer.HasReplayLoaded,
|
||||
Retries = RestartCount,
|
||||
OnPause = () => {
|
||||
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
|
||||
|
@ -155,7 +155,11 @@ namespace osu.Game.Screens.Select
|
||||
index = (index + direction + groups.Count) % groups.Count;
|
||||
if (groups[index].State != BeatmapGroupState.Hidden)
|
||||
{
|
||||
SelectBeatmap(groups[index].BeatmapPanels.First().Beatmap);
|
||||
if (skipDifficulties)
|
||||
SelectBeatmap(groups[index].SelectedPanel != null ? groups[index].SelectedPanel.Beatmap : groups[index].BeatmapPanels.First().Beatmap);
|
||||
else
|
||||
SelectBeatmap(direction == 1 ? groups[index].BeatmapPanels.First().Beatmap : groups[index].BeatmapPanels.Last().Beatmap);
|
||||
|
||||
return;
|
||||
}
|
||||
} while (index != startIndex);
|
||||
@ -167,10 +171,8 @@ namespace osu.Game.Screens.Select
|
||||
if (visibleGroups.Count < 1)
|
||||
return;
|
||||
BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)];
|
||||
BeatmapPanel panel = group?.BeatmapPanels.First();
|
||||
|
||||
if (panel == null)
|
||||
return;
|
||||
BeatmapPanel panel = group.BeatmapPanels[RNG.Next(group.BeatmapPanels.Count)];
|
||||
|
||||
selectGroup(group, panel);
|
||||
}
|
||||
@ -409,7 +411,14 @@ namespace osu.Game.Screens.Select
|
||||
int firstIndex = yPositions.BinarySearch(Current - Panel.MAX_HEIGHT);
|
||||
if (firstIndex < 0) firstIndex = ~firstIndex;
|
||||
int lastIndex = yPositions.BinarySearch(Current + drawHeight);
|
||||
if (lastIndex < 0) lastIndex = ~lastIndex;
|
||||
if (lastIndex < 0)
|
||||
{
|
||||
lastIndex = ~lastIndex;
|
||||
|
||||
// Add the first panel of the last visible beatmap group to preload its data.
|
||||
if (lastIndex != 0 && panels[lastIndex - 1] is BeatmapSetHeader)
|
||||
lastIndex++;
|
||||
}
|
||||
|
||||
// Add those panels within the previously found index range that should be displayed.
|
||||
for (int i = firstIndex; i < lastIndex; ++i)
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
@ -68,8 +69,6 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public Footer()
|
||||
{
|
||||
AlwaysReceiveInput = true;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = HEIGHT;
|
||||
Anchor = Anchor.BottomCentre;
|
||||
@ -124,5 +123,13 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
updateModeLight();
|
||||
}
|
||||
|
||||
protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || StartButton.Contains(screenSpacePos);
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
|
||||
|
||||
protected override bool OnClick(InputState state) => true;
|
||||
|
||||
protected override bool OnDragStart(InputState state) => true;
|
||||
}
|
||||
}
|
||||
|
@ -57,14 +57,23 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
beatmap?.Mods.BindTo(modSelect.SelectedMods);
|
||||
|
||||
if (Beatmap?.Track != null)
|
||||
Beatmap.Track.Looping = false;
|
||||
|
||||
beatmapDetails.Beatmap = beatmap;
|
||||
|
||||
if (beatmap?.Track != null)
|
||||
beatmap.Track.Looping = true;
|
||||
|
||||
base.OnBeatmapChanged(beatmap);
|
||||
}
|
||||
|
||||
protected override void OnResuming(Screen last)
|
||||
{
|
||||
player = null;
|
||||
|
||||
Beatmap.Track.Looping = true;
|
||||
|
||||
base.OnResuming(last);
|
||||
}
|
||||
|
||||
@ -83,13 +92,21 @@ namespace osu.Game.Screens.Select
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnExiting(next);
|
||||
if (base.OnExiting(next))
|
||||
return true;
|
||||
|
||||
if (Beatmap?.Track != null)
|
||||
Beatmap.Track.Looping = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnSelected()
|
||||
{
|
||||
if (player != null) return;
|
||||
|
||||
Beatmap.Track.Looping = false;
|
||||
|
||||
LoadComponentAsync(player = new PlayerLoader(new Player
|
||||
{
|
||||
Beatmap = Beatmap, //eagerly set this so it's present before push.
|
||||
|
@ -229,6 +229,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
changeBackground(Beatmap);
|
||||
|
||||
selectionChangeNoBounce = Beatmap?.BeatmapInfo;
|
||||
|
||||
Content.FadeInFromZero(250);
|
||||
|
||||
beatmapInfoWedge.State = Visibility.Visible;
|
||||
|
@ -74,10 +74,6 @@
|
||||
<Compile Include="Audio\SampleInfoList.cs" />
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
|
||||
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
|
||||
<Compile Include="Beatmaps\Events\BackgroundEvent.cs" />
|
||||
<Compile Include="Beatmaps\Events\BreakEvent.cs" />
|
||||
<Compile Include="Beatmaps\Events\Event.cs" />
|
||||
<Compile Include="Beatmaps\Events\EventInfo.cs" />
|
||||
<Compile Include="Online\API\Requests\PostMessageRequest.cs" />
|
||||
<Compile Include="Online\Chat\ErrorMessage.cs" />
|
||||
<Compile Include="Overlays\Chat\ChatTabControl.cs" />
|
||||
@ -85,12 +81,14 @@
|
||||
<Compile Include="Overlays\Music\PlaylistItem.cs" />
|
||||
<Compile Include="Overlays\Music\PlaylistList.cs" />
|
||||
<Compile Include="Overlays\OnScreenDisplay.cs" />
|
||||
<Compile Include="Graphics\Containers\SectionsContainer.cs" />
|
||||
<Compile Include="Overlays\Settings\SettingsHeader.cs" />
|
||||
<Compile Include="Overlays\Settings\Sections\Audio\MainMenuSettings.cs" />
|
||||
<Compile Include="Overlays\Toolbar\ToolbarChatButton.cs" />
|
||||
<Compile Include="Rulesets\Beatmaps\BeatmapConverter.cs" />
|
||||
<Compile Include="Rulesets\Beatmaps\BeatmapProcessor.cs" />
|
||||
<Compile Include="Beatmaps\Legacy\LegacyBeatmap.cs" />
|
||||
<Compile Include="Beatmaps\Timing\BreakPeriod.cs" />
|
||||
<Compile Include="Beatmaps\Timing\TimeSignatures.cs" />
|
||||
<Compile Include="Beatmaps\Timing\TimingInfo.cs" />
|
||||
<Compile Include="Database\BeatmapMetrics.cs" />
|
||||
@ -295,6 +293,7 @@
|
||||
<Compile Include="Graphics\UserInterface\RollingCounter.cs" />
|
||||
<Compile Include="Graphics\UserInterface\Volume\VolumeControlReceptor.cs" />
|
||||
<Compile Include="Graphics\Backgrounds\Background.cs" />
|
||||
<Compile Include="Graphics\Containers\BeatSyncedContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\ParallaxContainer.cs" />
|
||||
<Compile Include="Graphics\Cursor\MenuCursor.cs" />
|
||||
<Compile Include="Graphics\Processing\RatioAdjust.cs" />
|
||||
@ -442,11 +441,11 @@
|
||||
<Compile Include="Graphics\Containers\ReverseDepthFillFlowContainer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(SolutionDir)\osu-framework\osu.Framework\osu.Framework.csproj">
|
||||
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
|
||||
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
|
||||
<Name>osu.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<Project>{d9a367c9-4c1a-489f-9b05-a0cea2b53b58}</Project>
|
||||
<Name>osu.Game.Resources</Name>
|
||||
</ProjectReference>
|
||||
|
Reference in New Issue
Block a user