mirror of
https://github.com/ppy/osu.git
synced 2025-01-30 07:32:55 +08:00
Merge remote-tracking branch 'refs/remotes/ppy/master' into editor-playback-speed
This commit is contained in:
commit
0cad5a5ca3
@ -1 +1 @@
|
|||||||
Subproject commit c95b9350edb6305cfefdf08f902f6f73d336736b
|
Subproject commit 887db793c705b45071aea5e0c1cc931a7887165f
|
@ -28,16 +28,11 @@ namespace osu.Game.Beatmaps
|
|||||||
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||||
|
|
||||||
Mods.ValueChanged += mods => applyRateAdjustments();
|
Mods.ValueChanged += mods => applyRateAdjustments();
|
||||||
}
|
|
||||||
|
|
||||||
private void applyRateAdjustments()
|
beatmap = new Lazy<Beatmap>(populateBeatmap);
|
||||||
{
|
background = new Lazy<Texture>(populateBackground);
|
||||||
var t = track;
|
track = new Lazy<Track>(populateTrack);
|
||||||
if (t == null) return;
|
waveform = new Lazy<Waveform>(populateWaveform);
|
||||||
|
|
||||||
t.ResetSpeedAdjustments();
|
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
|
|
||||||
mod.ApplyToClock(t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Beatmap GetBeatmap();
|
protected abstract Beatmap GetBeatmap();
|
||||||
@ -45,98 +40,72 @@ namespace osu.Game.Beatmaps
|
|||||||
protected abstract Track GetTrack();
|
protected abstract Track GetTrack();
|
||||||
protected virtual Waveform GetWaveform() => new Waveform();
|
protected virtual Waveform GetWaveform() => new Waveform();
|
||||||
|
|
||||||
private Beatmap beatmap;
|
public bool BeatmapLoaded => beatmap.IsValueCreated;
|
||||||
private readonly object beatmapLock = new object();
|
public Beatmap Beatmap => beatmap.Value;
|
||||||
public Beatmap Beatmap
|
private readonly Lazy<Beatmap> beatmap;
|
||||||
|
|
||||||
|
private Beatmap populateBeatmap()
|
||||||
{
|
{
|
||||||
get
|
var b = GetBeatmap() ?? new Beatmap();
|
||||||
{
|
|
||||||
lock (beatmapLock)
|
|
||||||
{
|
|
||||||
if (beatmap != null) return beatmap;
|
|
||||||
|
|
||||||
beatmap = GetBeatmap() ?? new Beatmap();
|
// use the database-backed info.
|
||||||
|
b.BeatmapInfo = BeatmapInfo;
|
||||||
|
|
||||||
// use the database-backed info.
|
return b;
|
||||||
beatmap.BeatmapInfo = BeatmapInfo;
|
|
||||||
|
|
||||||
return beatmap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly object backgroundLock = new object();
|
public bool BackgroundLoaded => background.IsValueCreated;
|
||||||
private Texture background;
|
public Texture Background => background.Value;
|
||||||
public Texture Background
|
private Lazy<Texture> background;
|
||||||
|
|
||||||
|
private Texture populateBackground() => GetBackground();
|
||||||
|
|
||||||
|
public bool TrackLoaded => track.IsValueCreated;
|
||||||
|
public Track Track => track.Value;
|
||||||
|
private Lazy<Track> track;
|
||||||
|
|
||||||
|
private Track populateTrack()
|
||||||
{
|
{
|
||||||
get
|
// we want to ensure that we always have a track, even if it's a fake one.
|
||||||
{
|
var t = GetTrack() ?? new TrackVirtual();
|
||||||
lock (backgroundLock)
|
applyRateAdjustments(t);
|
||||||
{
|
return t;
|
||||||
return background ?? (background = GetBackground());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Track track;
|
public bool WaveformLoaded => waveform.IsValueCreated;
|
||||||
private readonly object trackLock = new object();
|
public Waveform Waveform => waveform.Value;
|
||||||
public Track Track
|
private readonly Lazy<Waveform> waveform;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (trackLock)
|
|
||||||
{
|
|
||||||
if (track != null) return track;
|
|
||||||
|
|
||||||
// we want to ensure that we always have a track, even if it's a fake one.
|
private Waveform populateWaveform() => GetWaveform();
|
||||||
track = GetTrack() ?? new TrackVirtual();
|
|
||||||
|
|
||||||
applyRateAdjustments();
|
|
||||||
return track;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Waveform waveform;
|
|
||||||
private readonly object waveformLock = new object();
|
|
||||||
public Waveform Waveform
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (waveformLock)
|
|
||||||
return waveform ?? (waveform = GetWaveform());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TrackLoaded => track != null;
|
|
||||||
|
|
||||||
public void TransferTo(WorkingBeatmap other)
|
public void TransferTo(WorkingBeatmap other)
|
||||||
{
|
{
|
||||||
lock (trackLock)
|
if (track.IsValueCreated && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
||||||
{
|
other.track = track;
|
||||||
if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
|
||||||
other.track = track;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
|
if (background.IsValueCreated && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
|
||||||
other.background = background;
|
other.background = background;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
{
|
{
|
||||||
background?.Dispose();
|
if (BackgroundLoaded) Background?.Dispose();
|
||||||
background = null;
|
if (WaveformLoaded) Waveform?.Dispose();
|
||||||
|
|
||||||
waveform?.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisposeTrack()
|
public void DisposeTrack()
|
||||||
{
|
{
|
||||||
lock (trackLock)
|
if (TrackLoaded) Track?.Dispose();
|
||||||
{
|
}
|
||||||
track?.Dispose();
|
|
||||||
track = null;
|
private void applyRateAdjustments(Track t = null)
|
||||||
}
|
{
|
||||||
|
if (t == null && track.IsValueCreated) t = Track;
|
||||||
|
if (t == null) return;
|
||||||
|
|
||||||
|
t.ResetSpeedAdjustments();
|
||||||
|
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
|
||||||
|
mod.ApplyToClock(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,12 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
var track = Beatmap.Value.Track;
|
if (!Beatmap.Value.TrackLoaded || !Beatmap.Value.BeatmapLoaded) return;
|
||||||
|
|
||||||
if (track == null)
|
var track = Beatmap.Value.Track;
|
||||||
|
var beatmap = Beatmap.Value.Beatmap;
|
||||||
|
|
||||||
|
if (track == null || beatmap == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;
|
double currentTrackTime = track.Length > 0 ? track.CurrentTime + EarlyActivationMilliseconds : Clock.CurrentTime;
|
||||||
|
@ -7,7 +7,7 @@ using OpenTK.Input;
|
|||||||
|
|
||||||
namespace osu.Game.Graphics.Containers
|
namespace osu.Game.Graphics.Containers
|
||||||
{
|
{
|
||||||
internal class OsuScrollContainer : ScrollContainer
|
public class OsuScrollContainer : ScrollContainer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows controlling the scroll bar from any position in the container using the right mouse button.
|
/// Allows controlling the scroll bar from any position in the container using the right mouse button.
|
||||||
|
@ -17,19 +17,21 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Leaderboards
|
namespace osu.Game.Screens.Select.Leaderboards
|
||||||
{
|
{
|
||||||
public class Leaderboard : Container
|
public class Leaderboard : Container
|
||||||
{
|
{
|
||||||
private readonly ScrollContainer scrollContainer;
|
private readonly ScrollContainer scrollContainer;
|
||||||
private readonly FillFlowContainer<LeaderboardScore> scrollFlow;
|
private FillFlowContainer<LeaderboardScore> scrollFlow;
|
||||||
|
|
||||||
public Action<Score> ScoreSelected;
|
public Action<Score> ScoreSelected;
|
||||||
|
|
||||||
private readonly LoadingAnimation loading;
|
private readonly LoadingAnimation loading;
|
||||||
|
|
||||||
private IEnumerable<Score> scores;
|
private IEnumerable<Score> scores;
|
||||||
|
|
||||||
public IEnumerable<Score> Scores
|
public IEnumerable<Score> Scores
|
||||||
{
|
{
|
||||||
get { return scores; }
|
get { return scores; }
|
||||||
@ -41,33 +43,43 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
int i = 150;
|
int i = 150;
|
||||||
if (scores == null)
|
if (scores == null)
|
||||||
{
|
{
|
||||||
foreach (var c in scrollFlow.Children)
|
if (scrollFlow != null)
|
||||||
c.FadeOut(i += 10);
|
{
|
||||||
|
foreach (var c in scrollFlow.Children)
|
||||||
|
c.FadeOut(i += 10);
|
||||||
|
|
||||||
foreach (var c in scrollFlow.Children)
|
foreach (var c in scrollFlow.Children)
|
||||||
c.LifetimeEnd = Time.Current + i;
|
c.LifetimeEnd = Time.Current + i;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollFlow.Clear();
|
// schedule because we may not be loaded yet (LoadComponentAsync complains).
|
||||||
|
Schedule(() =>
|
||||||
i = 0;
|
|
||||||
foreach (var s in scores)
|
|
||||||
{
|
{
|
||||||
var ls = new LeaderboardScore(s, 1 + i)
|
LoadComponentAsync(new FillFlowContainer<LeaderboardScore>
|
||||||
{
|
{
|
||||||
AlwaysPresent = true,
|
RelativeSizeAxes = Axes.X,
|
||||||
Action = () => ScoreSelected?.Invoke(s),
|
AutoSizeAxes = Axes.Y,
|
||||||
State = Visibility.Hidden,
|
Spacing = new Vector2(0f, 5f),
|
||||||
};
|
Padding = new MarginPadding { Top = 10, Bottom = 5 },
|
||||||
scrollFlow.Add(ls);
|
ChildrenEnumerable = scores.Select(s => new LeaderboardScore(s, 1 + i) { Action = () => ScoreSelected?.Invoke(s) })
|
||||||
|
}, f =>
|
||||||
|
{
|
||||||
|
scrollFlow?.Expire();
|
||||||
|
scrollContainer.Add(scrollFlow = f);
|
||||||
|
|
||||||
using (BeginDelayedSequence(i++ * 50, true))
|
i = 0;
|
||||||
ls.Show();
|
foreach (var s in f.Children)
|
||||||
}
|
{
|
||||||
|
using (s.BeginDelayedSequence(i++ * 50, true))
|
||||||
|
s.Show();
|
||||||
|
}
|
||||||
|
|
||||||
scrollContainer.ScrollTo(0f, false);
|
scrollContainer.ScrollTo(0f, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,16 +91,6 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ScrollbarVisible = false,
|
ScrollbarVisible = false,
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
scrollFlow = new FillFlowContainer<LeaderboardScore>
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Spacing = new Vector2(0f, 5f),
|
|
||||||
Padding = new MarginPadding { Top = 10, Bottom = 5 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
loading = new LoadingAnimation()
|
loading = new LoadingAnimation()
|
||||||
};
|
};
|
||||||
@ -152,6 +154,8 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
if (!scrollContainer.IsScrolledToEnd())
|
if (!scrollContainer.IsScrolledToEnd())
|
||||||
fadeStart -= LeaderboardScore.HEIGHT;
|
fadeStart -= LeaderboardScore.HEIGHT;
|
||||||
|
|
||||||
|
if (scrollFlow == null) return;
|
||||||
|
|
||||||
foreach (var c in scrollFlow.Children)
|
foreach (var c in scrollFlow.Children)
|
||||||
{
|
{
|
||||||
var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y;
|
var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y;
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -13,14 +14,13 @@ using osu.Framework.Input;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Leaderboards
|
namespace osu.Game.Screens.Select.Leaderboards
|
||||||
{
|
{
|
||||||
public class LeaderboardScore : OsuClickableContainer, IStateful<Visibility>
|
public class LeaderboardScore : OsuClickableContainer
|
||||||
{
|
{
|
||||||
public static readonly float HEIGHT = 60;
|
public static readonly float HEIGHT = 60;
|
||||||
|
|
||||||
@ -34,72 +34,16 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
private const float background_alpha = 0.25f;
|
private const float background_alpha = 0.25f;
|
||||||
private const float rank_width = 30;
|
private const float rank_width = 30;
|
||||||
|
|
||||||
private readonly Box background;
|
private Box background;
|
||||||
private readonly Container content;
|
private Container content;
|
||||||
private readonly Container avatar;
|
private Container avatar;
|
||||||
private readonly DrawableRank scoreRank;
|
private DrawableRank scoreRank;
|
||||||
private readonly OsuSpriteText nameLabel;
|
private OsuSpriteText nameLabel;
|
||||||
private readonly GlowingSpriteText scoreLabel;
|
private GlowingSpriteText scoreLabel;
|
||||||
private readonly ScoreComponentLabel maxCombo;
|
private ScoreComponentLabel maxCombo;
|
||||||
private readonly ScoreComponentLabel accuracy;
|
private ScoreComponentLabel accuracy;
|
||||||
private readonly Container flagBadgeContainer;
|
private Container flagBadgeContainer;
|
||||||
private readonly FillFlowContainer<ModIcon> modsContainer;
|
private FillFlowContainer<ModIcon> modsContainer;
|
||||||
|
|
||||||
private Visibility state;
|
|
||||||
|
|
||||||
public Visibility State
|
|
||||||
{
|
|
||||||
get { return state; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (state == value)
|
|
||||||
return;
|
|
||||||
state = value;
|
|
||||||
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case Visibility.Hidden:
|
|
||||||
foreach (var d in new Drawable[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
|
|
||||||
d.FadeOut();
|
|
||||||
|
|
||||||
Alpha = 0;
|
|
||||||
|
|
||||||
content.MoveToY(75);
|
|
||||||
avatar.MoveToX(75);
|
|
||||||
nameLabel.MoveToX(150);
|
|
||||||
break;
|
|
||||||
case Visibility.Visible:
|
|
||||||
this.FadeIn(200);
|
|
||||||
content.MoveToY(0, 800, Easing.OutQuint);
|
|
||||||
|
|
||||||
using (BeginDelayedSequence(100, true))
|
|
||||||
{
|
|
||||||
avatar.FadeIn(300, Easing.OutQuint);
|
|
||||||
nameLabel.FadeIn(350, Easing.OutQuint);
|
|
||||||
|
|
||||||
avatar.MoveToX(0, 300, Easing.OutQuint);
|
|
||||||
nameLabel.MoveToX(0, 350, Easing.OutQuint);
|
|
||||||
|
|
||||||
using (BeginDelayedSequence(250, true))
|
|
||||||
{
|
|
||||||
scoreLabel.FadeIn(200);
|
|
||||||
scoreRank.FadeIn(200);
|
|
||||||
|
|
||||||
using (BeginDelayedSequence(50, true))
|
|
||||||
{
|
|
||||||
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, };
|
|
||||||
for (int i = 0; i < drawables.Length; i++)
|
|
||||||
drawables[i].FadeIn(100 + i * 50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
StateChanged?.Invoke(State);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public LeaderboardScore(Score score, int rank)
|
public LeaderboardScore(Score score, int rank)
|
||||||
{
|
{
|
||||||
@ -108,7 +52,11 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = HEIGHT;
|
Height = HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
@ -255,23 +203,51 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
|
ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (Mod mod in Score.Mods)
|
|
||||||
{
|
|
||||||
modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.375f) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleVisibility() => State = State == Visibility.Visible ? Visibility.Hidden : Visibility.Visible;
|
public override void Show()
|
||||||
|
{
|
||||||
|
foreach (var d in new Drawable[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
|
||||||
|
d.FadeOut();
|
||||||
|
|
||||||
public override void Hide() => State = Visibility.Hidden;
|
Alpha = 0;
|
||||||
public override void Show() => State = Visibility.Visible;
|
|
||||||
|
content.MoveToY(75);
|
||||||
|
avatar.MoveToX(75);
|
||||||
|
nameLabel.MoveToX(150);
|
||||||
|
|
||||||
|
this.FadeIn(200);
|
||||||
|
content.MoveToY(0, 800, Easing.OutQuint);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(100, true))
|
||||||
|
{
|
||||||
|
avatar.FadeIn(300, Easing.OutQuint);
|
||||||
|
nameLabel.FadeIn(350, Easing.OutQuint);
|
||||||
|
|
||||||
|
avatar.MoveToX(0, 300, Easing.OutQuint);
|
||||||
|
nameLabel.MoveToX(0, 350, Easing.OutQuint);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(250, true))
|
||||||
|
{
|
||||||
|
scoreLabel.FadeIn(200);
|
||||||
|
scoreRank.FadeIn(200);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(50, true))
|
||||||
|
{
|
||||||
|
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, };
|
||||||
|
for (int i = 0; i < drawables.Length; i++)
|
||||||
|
drawables[i].FadeIn(100 + i * 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
protected override bool OnHover(InputState state)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user