1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 14:13:20 +08:00

Resolve merge conflict

This commit is contained in:
TocoToucan 2018-01-13 22:32:56 +03:00
commit d3afbdaac6
120 changed files with 1862 additions and 1576 deletions

@ -1 +1 @@
Subproject commit 65947291229541de3eb1aff0e703f6968b07f976 Subproject commit 49b563e2cf170eb19006b98dd5b69c2398362d9e

View File

@ -43,7 +43,6 @@ namespace osu.Desktop
host.Run(new OsuGameDesktop(args)); host.Run(new OsuGameDesktop(args));
break; break;
} }
} }
return 0; return 0;
} }

View File

@ -11,7 +11,7 @@ using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps namespace osu.Game.Rulesets.Catch.Beatmaps
{ {
internal class CatchBeatmapConverter : BeatmapConverter<CatchHitObject> public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
{ {
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
var curveData = obj as IHasCurve; var curveData = obj as IHasCurve;
var positionData = obj as IHasXPosition; var positionData = obj as IHasXPosition;
var comboData = obj as IHasCombo; var comboData = obj as IHasCombo;
var endTime = obj as IHasEndTime;
if (positionData == null) if (positionData == null)
yield break; yield break;
@ -42,6 +43,19 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
yield break; yield break;
} }
if (endTime != null)
{
yield return new BananaShower
{
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false
};
yield break;
}
yield return new Fruit yield return new Fruit
{ {
StartTime = obj.StartTime, StartTime = obj.StartTime,

View File

@ -12,7 +12,7 @@ using OpenTK;
namespace osu.Game.Rulesets.Catch.Beatmaps namespace osu.Game.Rulesets.Catch.Beatmaps
{ {
internal class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject> public class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject>
{ {
public override void PostProcess(Beatmap<CatchHitObject> beatmap) public override void PostProcess(Beatmap<CatchHitObject> beatmap)
{ {

View File

@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Catch
[Description("Move right")] [Description("Move right")]
MoveRight, MoveRight,
[Description("Engage dash")] [Description("Engage dash")]
Dash Dash,
PositionUpdate
} }
} }

View File

@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Catch
{ {
Mods = new Mod[] Mods = new Mod[]
{ {
new ModAutoplay(), new CatchModAutoplay(),
new ModCinema(), new ModCinema(),
}, },
}, },

View File

@ -0,0 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
{
protected override Score CreateReplayScore(Beatmap<CatchHitObject> beatmap)
{
return new Score
{
User = new User { Username = "osu!salad!" },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
}
}
}

View File

@ -0,0 +1,63 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects
{
public class BananaShower : CatchHitObject, IHasEndTime
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
public override bool LastInCombo => true;
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();
createBananas();
}
private void createBananas()
{
double spacing = Duration;
while (spacing > 100)
spacing /= 2;
if (spacing <= 0)
return;
for (double i = StartTime; i <= EndTime; i += spacing)
AddNested(new Banana
{
Samples = Samples,
ComboColour = getNextComboColour(),
StartTime = i,
X = RNG.NextSingle()
});
}
private Color4 getNextComboColour()
{
switch (RNG.Next(0, 3))
{
default:
return new Color4(255, 240, 0, 255);
case 1:
return new Color4(255, 192, 0, 255);
case 2:
return new Color4(214, 221, 28, 255);
}
}
public double EndTime => StartTime + Duration;
public double Duration { get; set; }
public class Banana : Fruit
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
}
}
}

View File

@ -0,0 +1,44 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableBananaShower : DrawableCatchHitObject<BananaShower>
{
private readonly Container bananaContainer;
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
: base(s)
{
RelativeSizeAxes = Axes.X;
Origin = Anchor.BottomLeft;
X = 0;
Child = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
AddNested(getVisualRepresentation?.Invoke(b));
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
AddJudgement(new Judgement { Result = NestedHitObjects.Cast<DrawableCatchHitObject>().Any(n => n.Judgements.Any(j => j.IsHit)) ? HitResult.Perfect : HitResult.Miss });
}
protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
bananaContainer.Add(h);
base.AddNested(h);
}
}
}

View File

@ -5,11 +5,24 @@ using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK; using OpenTK;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
public abstract class PalpableCatchHitObject<TObject> : DrawableCatchHitObject<TObject>
where TObject : CatchHitObject
{
public override bool CanBePlated => true;
protected PalpableCatchHitObject(TObject hitObject)
: base(hitObject)
{
Scale = new Vector2(HitObject.Scale);
}
}
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
where TObject : CatchHitObject where TObject : CatchHitObject
{ {
@ -19,27 +32,29 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: base(hitObject) : base(hitObject)
{ {
HitObject = hitObject; HitObject = hitObject;
Anchor = Anchor.BottomLeft;
Scale = new Vector2(HitObject.Scale);
} }
} }
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchHitObject> public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
{ {
public virtual bool CanBePlated => false;
protected DrawableCatchHitObject(CatchHitObject hitObject) protected DrawableCatchHitObject(CatchHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
RelativePositionAxes = Axes.Both; RelativePositionAxes = Axes.X;
X = hitObject.X; X = hitObject.X;
Y = (float)HitObject.StartTime;
} }
public Func<CatchHitObject, bool> CheckPosition; public Func<CatchHitObject, bool> CheckPosition;
protected override void CheckForJudgements(bool userTriggered, double timeOffset) protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{ {
if (CheckPosition == null) return;
if (timeOffset > 0) if (timeOffset > 0)
AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss }); AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
} }
private const float preempt = 1000; private const float preempt = 1000;
@ -47,17 +62,21 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
using (BeginAbsoluteSequence(HitObject.StartTime - preempt)) using (BeginAbsoluteSequence(HitObject.StartTime - preempt))
{
// animation
this.FadeIn(200); this.FadeIn(200);
}
switch (state) var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
using (BeginAbsoluteSequence(endTime, true))
{ {
case ArmedState.Miss: switch (state)
using (BeginAbsoluteSequence(HitObject.StartTime, true)) {
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out); case ArmedState.Miss:
break; this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
break;
case ArmedState.Hit:
this.FadeOut().Expire();
break;
}
} }
} }
} }

View File

@ -8,7 +8,7 @@ using OpenTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
public class DrawableDroplet : DrawableCatchHitObject<Droplet> public class DrawableDroplet : PalpableCatchHitObject<Droplet>
{ {
public DrawableDroplet(Droplet h) public DrawableDroplet(Droplet h)
: base(h) : base(h)

View File

@ -14,7 +14,7 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
public class DrawableFruit : DrawableCatchHitObject<Fruit> public class DrawableFruit : PalpableCatchHitObject<Fruit>
{ {
private Circle border; private Circle border;
@ -243,6 +243,27 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}, },
} }
}; };
case FruitVisualRepresentation.Banana:
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
Y = -0.15f
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4 * 1.2f, large_pulp_4 * 3),
},
}
};
} }
} }

View File

@ -1,10 +1,10 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using OpenTK;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
@ -13,41 +13,26 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
private readonly Container dropletContainer; private readonly Container dropletContainer;
public DrawableJuiceStream(JuiceStream s) : base(s) public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
: base(s)
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration; Origin = Anchor.BottomLeft;
X = 0; X = 0;
Child = dropletContainer = new Container Child = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
{
RelativeSizeAxes = Axes.Both,
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
};
foreach (CatchHitObject tick in s.NestedHitObjects.OfType<CatchHitObject>()) foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
{ AddNested(getVisualRepresentation?.Invoke(o));
TinyDroplet tiny = tick as TinyDroplet;
if (tiny != null)
{
AddNested(new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) });
continue;
}
Droplet droplet = tick as Droplet;
if (droplet != null)
AddNested(new DrawableDroplet(droplet));
Fruit fruit = tick as Fruit;
if (fruit != null)
AddNested(new DrawableFruit(fruit));
}
} }
protected override void AddNested(DrawableHitObject<CatchHitObject> h) protected override void AddNested(DrawableHitObject h)
{ {
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false; var catchObject = (DrawableCatchHitObject)h;
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
catchObject.AccentColour = HitObject.ComboColour;
dropletContainer.Add(h); dropletContainer.Add(h);
base.AddNested(h); base.AddNested(h);
} }

View File

@ -125,10 +125,8 @@ namespace osu.Game.Rulesets.Catch.Objects
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
}); });
} }
} }
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity; public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH; public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;

View File

@ -0,0 +1,54 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Catch.Replays
{
internal class CatchAutoGenerator : AutoGenerator<CatchHitObject>
{
public const double RELEASE_DELAY = 20;
public CatchAutoGenerator(Beatmap<CatchHitObject> beatmap)
: base(beatmap)
{
Replay = new Replay { User = new User { Username = @"Autoplay" } };
}
protected Replay Replay;
public override Replay Generate()
{
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
Replay.Frames.Add(new CatchReplayFrame(-100000, 0));
foreach (var obj in Beatmap.HitObjects)
{
switch (obj)
{
case Fruit _:
Replay.Frames.Add(new CatchReplayFrame(obj.StartTime, obj.X));
break;
}
foreach (var nestedObj in obj.NestedHitObjects.Cast<CatchHitObject>())
{
switch (nestedObj)
{
case BananaShower.Banana _:
case TinyDroplet _:
case Droplet _:
Replay.Frames.Add(new CatchReplayFrame(nestedObj.StartTime, nestedObj.X));
break;
}
}
}
return Replay;
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Input;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Catch.Replays
{
public class CatchFramedReplayInputHandler : FramedReplayInputHandler
{
public CatchFramedReplayInputHandler(Replay replay)
: base(replay)
{
}
public override List<InputState> GetPendingStates() => new List<InputState>
{
new CatchReplayState
{
PressedActions = new List<CatchAction> { CatchAction.PositionUpdate },
CatcherX = ((CatchReplayFrame)CurrentFrame).MouseX
},
new CatchReplayState { PressedActions = new List<CatchAction>() },
};
public class CatchReplayState : ReplayState<CatchAction>
{
public float? CatcherX { get; set; }
}
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Catch.Replays
{
public class CatchReplayFrame : ReplayFrame
{
public override bool IsImportant => MouseX > 0;
public CatchReplayFrame(double time, float? x = null)
: base(time, x ?? -1, null, ReplayButtonState.None)
{
}
}
}

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring namespace osu.Game.Rulesets.Catch.Scoring
{ {
internal class CatchScoreProcessor : ScoreProcessor<CatchHitObject> public class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
{ {
public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer) public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer)
: base(rulesetContainer) : base(rulesetContainer)
@ -21,23 +21,25 @@ namespace osu.Game.Rulesets.Catch.Scoring
{ {
foreach (var obj in beatmap.HitObjects) foreach (var obj in beatmap.HitObjects)
{ {
var stream = obj as JuiceStream; switch (obj)
if (stream != null)
{ {
AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); case JuiceStream stream:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
foreach (var unused in stream.NestedHitObjects.OfType<CatchHitObject>())
AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
continue; foreach (var _ in stream.NestedHitObjects.Cast<CatchHitObject>())
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
case BananaShower shower:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
case Fruit _:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
} }
var fruit = obj as Fruit;
if (fruit != null)
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
} }
base.SimulateAutoplay(beatmap); base.SimulateAutoplay(beatmap);

View File

@ -0,0 +1,50 @@
// Copyright (c) 2007-2018 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 NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.UI;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
[Ignore("getting CI working")]
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(BananaShower),
typeof(DrawableBananaShower),
typeof(CatchRuleset),
typeof(CatchRulesetContainer),
};
public TestCaseBananaShower()
: base(new CatchRuleset())
{
}
protected override Beatmap CreateBeatmap()
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 6,
}
}
};
beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true });
return beatmap;
}
}
}

View File

@ -1,14 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.UI;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
@ -21,13 +22,11 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea; private readonly CatcherArea catcherArea;
public CatchPlayfield(BeatmapDifficulty difficulty) public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
: base(Axes.Y, BASE_WIDTH) : base(ScrollingDirection.Down, BASE_WIDTH)
{ {
Container explodingFruitContainer; Container explodingFruitContainer;
Reversed.Value = true;
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
@ -46,6 +45,7 @@ namespace osu.Game.Rulesets.Catch.UI
}, },
catcherArea = new CatcherArea(difficulty) catcherArea = new CatcherArea(difficulty)
{ {
GetVisualRepresentation = getVisualRepresentation,
ExplodingFruitTarget = explodingFruitContainer, ExplodingFruitTarget = explodingFruitContainer,
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft, Origin = Anchor.TopLeft,

View File

@ -6,10 +6,14 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
@ -22,23 +26,31 @@ namespace osu.Game.Rulesets.Catch.UI
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor(); protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter(); protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty); protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation);
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h) protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
{ {
var fruit = h as Fruit; switch (h)
if (fruit != null) {
return new DrawableFruit(fruit); case Fruit fruit:
return new DrawableFruit(fruit);
var stream = h as JuiceStream; case JuiceStream stream:
if (stream != null) return new DrawableJuiceStream(stream, GetVisualRepresentation);
return new DrawableJuiceStream(stream); case BananaShower banana:
return new DrawableBananaShower(banana, GetVisualRepresentation);
case TinyDroplet tiny:
return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) };
case Droplet droplet:
return new DrawableDroplet(droplet);
}
return null; return null;
} }

View File

@ -13,6 +13,7 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using OpenTK; using OpenTK;
@ -20,12 +21,14 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.UI namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatcherArea : Container public class CatcherArea : Container, IKeyBindingHandler<CatchAction>
{ {
public const float CATCHER_SIZE = 172; public const float CATCHER_SIZE = 172;
protected readonly Catcher MovableCatcher; protected readonly Catcher MovableCatcher;
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> GetVisualRepresentation;
public Container ExplodingFruitTarget public Container ExplodingFruitTarget
{ {
set { MovableCatcher.ExplodingFruitTarget = value; } set { MovableCatcher.ExplodingFruitTarget = value; }
@ -41,36 +44,60 @@ namespace osu.Game.Rulesets.Catch.UI
}; };
} }
private DrawableCatchHitObject lastPlateableFruit;
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement) public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
{ {
if (judgement.IsHit) if (judgement.IsHit && fruit.CanBePlated)
{ {
var screenSpacePosition = fruit.ScreenSpaceDrawQuad.Centre; var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
// todo: make this less ugly, somehow. if (caughtFruit == null) return;
(fruit.Parent as Container<DrawableHitObject>)?.Remove(fruit);
(fruit.Parent as Container)?.Remove(fruit);
fruit.RelativePositionAxes = Axes.None; caughtFruit.AccentColour = fruit.AccentColour;
fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(screenSpacePosition).X - MovableCatcher.DrawSize.X / 2, 0); caughtFruit.RelativePositionAxes = Axes.None;
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
fruit.Anchor = Anchor.TopCentre; caughtFruit.Anchor = Anchor.TopCentre;
fruit.Origin = Anchor.Centre; caughtFruit.Origin = Anchor.Centre;
fruit.Scale *= 0.7f; caughtFruit.Scale *= 0.7f;
fruit.LifetimeEnd = double.MaxValue; caughtFruit.LifetimeEnd = double.MaxValue;
MovableCatcher.Add(fruit); MovableCatcher.Add(caughtFruit);
lastPlateableFruit = caughtFruit;
} }
if (fruit.HitObject.LastInCombo) if (fruit.HitObject.LastInCombo)
{ {
if (judgement.IsHit) if (judgement.IsHit)
MovableCatcher.Explode(); {
// this is required to make this run after the last caught fruit runs UpdateState at least once.
// TODO: find a better alternative
if (lastPlateableFruit.IsLoaded)
MovableCatcher.Explode();
else
lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); };
}
else else
MovableCatcher.Drop(); MovableCatcher.Drop();
} }
} }
public bool OnPressed(CatchAction action)
{
if (action != CatchAction.PositionUpdate) return false;
CatchFramedReplayInputHandler.CatchReplayState state = (CatchFramedReplayInputHandler.CatchReplayState)GetContainingInputManager().CurrentState;
if (state.CatcherX.HasValue)
MovableCatcher.X = state.CatcherX.Value;
return true;
}
public bool OnReleased(CatchAction action) => false;
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
public class Catcher : Container, IKeyBindingHandler<CatchAction> public class Catcher : Container, IKeyBindingHandler<CatchAction>
@ -193,7 +220,7 @@ namespace osu.Game.Rulesets.Catch.UI
while (caughtFruit.Any(f => while (caughtFruit.Any(f =>
f.LifetimeEnd == double.MaxValue && f.LifetimeEnd == double.MaxValue &&
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2))) Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
{ {
float diff = (ourRadius + theirRadius) / allowance; float diff = (ourRadius + theirRadius) / allowance;
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff; fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;

View File

@ -60,11 +60,17 @@
<Compile Include="Mods\CatchModPerfect.cs" /> <Compile Include="Mods\CatchModPerfect.cs" />
<Compile Include="Mods\CatchModRelax.cs" /> <Compile Include="Mods\CatchModRelax.cs" />
<Compile Include="Mods\CatchModSuddenDeath.cs" /> <Compile Include="Mods\CatchModSuddenDeath.cs" />
<Compile Include="Mods\CatchModAutoplay.cs" />
<Compile Include="Objects\BananaShower.cs" />
<Compile Include="Objects\Drawable\DrawableBananaShower.cs" />
<Compile Include="Objects\Drawable\DrawableCatchHitObject.cs" /> <Compile Include="Objects\Drawable\DrawableCatchHitObject.cs" />
<Compile Include="Objects\Drawable\DrawableDroplet.cs" /> <Compile Include="Objects\Drawable\DrawableDroplet.cs" />
<Compile Include="Objects\Drawable\DrawableJuiceStream.cs" /> <Compile Include="Objects\Drawable\DrawableJuiceStream.cs" />
<Compile Include="Objects\Drawable\Pieces\Pulp.cs" /> <Compile Include="Objects\Drawable\Pieces\Pulp.cs" />
<Compile Include="Objects\JuiceStream.cs" /> <Compile Include="Objects\JuiceStream.cs" />
<Compile Include="Replays\CatchAutoGenerator.cs" />
<Compile Include="Replays\CatchFramedReplayInputHandler.cs" />
<Compile Include="Replays\CatchReplayFrame.cs" />
<Compile Include="Scoring\CatchScoreProcessor.cs" /> <Compile Include="Scoring\CatchScoreProcessor.cs" />
<Compile Include="Judgements\CatchJudgement.cs" /> <Compile Include="Judgements\CatchJudgement.cs" />
<Compile Include="Objects\CatchHitObject.cs" /> <Compile Include="Objects\CatchHitObject.cs" />
@ -73,6 +79,7 @@
<Compile Include="Objects\Fruit.cs" /> <Compile Include="Objects\Fruit.cs" />
<Compile Include="Objects\TinyDroplet.cs" /> <Compile Include="Objects\TinyDroplet.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tests\TestCaseBananaShower.cs" />
<Compile Include="Tests\TestCaseCatcherArea.cs" /> <Compile Include="Tests\TestCaseCatcherArea.cs" />
<Compile Include="Tests\TestCaseCatchStacker.cs" /> <Compile Include="Tests\TestCaseCatchStacker.cs" />
<Compile Include="Tests\TestCaseFruitObjects.cs" /> <Compile Include="Tests\TestCaseFruitObjects.cs" />

View File

@ -58,7 +58,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original) protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
{ {
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);

View File

@ -95,7 +95,6 @@ namespace osu.Game.Rulesets.Mania
new ModCinema(), new ModCinema(),
}, },
}, },
new ManiaModGravity()
}; };
default: default:

View File

@ -1,23 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Timing;
namespace osu.Game.Rulesets.Mania.Mods
{
/// <summary>
/// A type of mod which generates speed adjustments that scroll the hit objects and bar lines.
/// </summary>
internal interface IGenerateSpeedAdjustments
{
/// <summary>
/// Applies this mod to a hit renderer.
/// </summary>
/// <param name="rulesetContainer">The hit renderer to apply to.</param>
/// <param name="hitObjectTimingChanges">The per-column list of speed adjustments for hit objects.</param>
/// <param name="barlineTimingChanges">The list of speed adjustments for bar lines.</param>
void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges);
}
}

View File

@ -8,7 +8,7 @@ using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public abstract class ManiaKeyMod : Mod, IApplicableMod, IApplicableToBeatmapConverter<ManiaHitObject> public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter<ManiaHitObject>
{ {
public override string ShortenedName => Name; public override string ShortenedName => Name;
public abstract int KeyCount { get; } public abstract int KeyCount { get; }

View File

@ -1,48 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Timing;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
{
public override string Name => "Gravity";
public override string ShortenedName => "GR";
public override double ScoreMultiplier => 0;
public override FontAwesome Icon => FontAwesome.fa_sort_desc;
public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges,
ref List<SpeedAdjustmentContainer> barlineTimingChanges)
{
// We have to generate one speed adjustment per hit object for gravity
foreach (var obj in rulesetContainer.Objects.OfType<ManiaHitObject>())
{
var controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime);
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
controlPoint.TimingPoint.BeatLength = 1000;
hitObjectTimingChanges[obj.Column].Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
}
// Like with hit objects, we need to generate one speed adjustment per bar line
foreach (var barLine in rulesetContainer.BarLines)
{
var controlPoint = rulesetContainer.CreateControlPointAt(barLine.HitObject.StartTime);
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
controlPoint.TimingPoint.BeatLength = 1000;
barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
}
}
}
}

View File

@ -7,7 +7,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics; using OpenTK.Graphics;
using OpenTK;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
@ -42,8 +41,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public DrawableHoldNote(HoldNote hitObject, ManiaAction action) public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
: base(hitObject, action) : base(hitObject, action)
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.X;
Height = (float)HitObject.Duration;
AddRange(new Drawable[] AddRange(new Drawable[]
{ {
@ -60,12 +58,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
}, },
tickContainer = new Container<DrawableHoldNoteTick> tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
{
RelativeSizeAxes = Axes.Both,
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
},
head = new DrawableHeadNote(this, action) head = new DrawableHeadNote(this, action)
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
@ -73,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}, },
tail = new DrawableTailNote(this, action) tail = new DrawableTailNote(this, action)
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre Origin = Anchor.TopCentre
} }
}); });
@ -175,13 +168,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
this.holdNote = holdNote; this.holdNote = holdNote;
RelativePositionAxes = Axes.None;
Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
GlowPiece.Alpha = 0; GlowPiece.Alpha = 0;
} }
@ -200,6 +186,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return true; return true;
} }
protected override void UpdateState(ArmedState state)
{
// The holdnote keeps scrolling through for now, so having the head disappear looks weird
}
} }
/// <summary> /// <summary>
@ -214,13 +205,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
this.holdNote = holdNote; this.holdNote = holdNote;
RelativePositionAxes = Axes.None;
Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
GlowPiece.Alpha = 0; GlowPiece.Alpha = 0;
} }
@ -252,6 +236,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}); });
} }
protected override void UpdateState(ArmedState state)
{
// The holdnote keeps scrolling through, so having the tail disappear looks weird
}
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
public override bool OnReleased(ManiaAction action) public override bool OnReleased(ManiaAction action)

View File

@ -32,15 +32,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
Y = (float)HitObject.StartTime;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Size = new Vector2(1); Size = new Vector2(1);
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
Children = new[] Children = new[]
{ {
glowContainer = new CircularContainer glowContainer = new CircularContainer

View File

@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
public abstract class DrawableManiaHitObject<TObject> : DrawableScrollingHitObject<ManiaHitObject> public abstract class DrawableManiaHitObject<TObject> : DrawableHitObject<ManiaHitObject>
where TObject : ManiaHitObject where TObject : ManiaHitObject
{ {
/// <summary> /// <summary>
@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null) protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
: base(hitObject) : base(hitObject)
{ {
RelativePositionAxes = Axes.Y; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
HitObject = hitObject; HitObject = hitObject;
if (action != null) if (action != null)

View File

@ -78,6 +78,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
switch (state)
{
case ArmedState.Hit:
case ArmedState.Miss:
this.FadeOut(100).Expire();
break;
}
} }
public virtual bool OnPressed(ManiaAction action) public virtual bool OnPressed(ManiaAction action)

View File

@ -93,7 +93,6 @@ namespace osu.Game.Rulesets.Mania.Objects
Column = Column Column = Column
}); });
} }
} }
/// <summary> /// <summary>

View File

@ -5,16 +5,13 @@ using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
@ -44,10 +41,10 @@ namespace osu.Game.Rulesets.Mania.Tests
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right)); AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true)); AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
AddStep("Notes with input", () => createPlayfieldWithNotes(false)); AddStep("Notes with input", () => createPlayfieldWithNotes());
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true)); AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true)); AddStep("Notes with gravity", () => createPlayfieldWithNotes());
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true)); AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true));
AddStep("Hit explosion", () => AddStep("Hit explosion", () =>
{ {
@ -70,11 +67,6 @@ namespace osu.Game.Rulesets.Mania.Tests
maniaRuleset = rulesets.GetRuleset(3); maniaRuleset = rulesets.GetRuleset(3);
} }
private SpeedAdjustmentContainer createTimingChange(double time, bool gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
{
TimingPoint = { BeatLength = 1000 }
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false) private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
{ {
Clear(); Clear();
@ -95,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Tests
return playfield; return playfield;
} }
private void createPlayfieldWithNotes(bool gravity, bool inverted = false) private void createPlayfieldWithNotes(bool inverted = false)
{ {
Clear(); Clear();
@ -114,23 +106,14 @@ namespace osu.Game.Rulesets.Mania.Tests
playfield.Inverted.Value = inverted; playfield.Inverted.Value = inverted;
if (!gravity)
playfield.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
for (double t = start_time; t <= start_time + duration; t += 100) for (double t = start_time; t <= start_time + duration; t += 100)
{ {
if (gravity)
playfield.Columns.ElementAt(0).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note playfield.Add(new DrawableNote(new Note
{ {
StartTime = t, StartTime = t,
Column = 0 Column = 0
}, ManiaAction.Key1)); }, ManiaAction.Key1));
if (gravity)
playfield.Columns.ElementAt(3).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note playfield.Add(new DrawableNote(new Note
{ {
StartTime = t, StartTime = t,
@ -138,9 +121,6 @@ namespace osu.Game.Rulesets.Mania.Tests
}, ManiaAction.Key4)); }, ManiaAction.Key4));
} }
if (gravity)
playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote playfield.Add(new DrawableHoldNote(new HoldNote
{ {
StartTime = start_time, StartTime = start_time,
@ -148,9 +128,6 @@ namespace osu.Game.Rulesets.Mania.Tests
Column = 1 Column = 1
}, ManiaAction.Key2)); }, ManiaAction.Key2));
if (gravity)
playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote playfield.Add(new DrawableHoldNote(new HoldNote
{ {
StartTime = start_time, StartTime = start_time,

View File

@ -1,60 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Timing;
namespace osu.Game.Rulesets.Mania.Timing
{
/// <summary>
/// A <see cref="ScrollingContainer"/> that emulates a form of gravity where hit objects speed up over time.
/// </summary>
internal class GravityScrollingContainer : ScrollingContainer
{
private readonly MultiplierControlPoint controlPoint;
public GravityScrollingContainer(MultiplierControlPoint controlPoint)
{
this.controlPoint = controlPoint;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
// The gravity-adjusted start position
float startPos = (float)computeGravityTime(controlPoint.StartTime);
// The gravity-adjusted end position
float endPos = (float)computeGravityTime(controlPoint.StartTime + RelativeChildSize.Y);
Y = startPos;
Height = endPos - startPos;
}
/// <summary>
/// Applies gravity to a time value based on the current time.
/// </summary>
/// <param name="time">The time value gravity should be applied to.</param>
/// <returns>The time after gravity is applied to <paramref name="time"/>.</returns>
private double computeGravityTime(double time)
{
double relativeTime = relativeTimeAt(time);
// The sign of the relative time, this is used to apply backwards acceleration leading into startTime
double sign = relativeTime < 0 ? -1 : 1;
return VisibleTimeRange - acceleration * relativeTime * relativeTime * sign;
}
/// <summary>
/// The acceleration due to "gravity" of the content of this container.
/// </summary>
private double acceleration => 1 / VisibleTimeRange;
/// <summary>
/// Computes the current time relative to <paramref name="time"/>, accounting for <see cref="ScrollingContainer.VisibleTimeRange"/>.
/// </summary>
/// <param name="time">The non-offset time.</param>
/// <returns>The current time relative to <paramref name="time"/> - <see cref="ScrollingContainer.VisibleTimeRange"/>. </returns>
private double relativeTimeAt(double time) => Time.Current - time + VisibleTimeRange;
}
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Timing;
namespace osu.Game.Rulesets.Mania.Timing
{
public class ManiaSpeedAdjustmentContainer : SpeedAdjustmentContainer
{
private readonly ScrollingAlgorithm scrollingAlgorithm;
public ManiaSpeedAdjustmentContainer(MultiplierControlPoint timingSection, ScrollingAlgorithm scrollingAlgorithm)
: base(timingSection)
{
this.scrollingAlgorithm = scrollingAlgorithm;
}
protected override ScrollingContainer CreateScrollingContainer()
{
switch (scrollingAlgorithm)
{
default:
return base.CreateScrollingContainer();
case ScrollingAlgorithm.Gravity:
return new GravityScrollingContainer(ControlPoint);
}
}
}
}

View File

@ -1,17 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Mania.Timing
{
public enum ScrollingAlgorithm
{
/// <summary>
/// Basic scrolling algorithm based on the timing section time. This is the default algorithm.
/// </summary>
Basic,
/// <summary>
/// Emulating a form of gravity where hit objects speed up over time.
/// </summary>
Gravity
}
}

View File

@ -12,8 +12,8 @@ using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using System; using System;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Mania.UI
private const float opacity_pressed = 0.25f; private const float opacity_pressed = 0.25f;
public Column() public Column()
: base(Axes.Y) : base(ScrollingDirection.Up)
{ {
Width = column_width; Width = column_width;

View File

@ -3,7 +3,6 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -17,6 +16,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly int columnCount; private readonly int columnCount;
public ManiaPlayfield(int columnCount) public ManiaPlayfield(int columnCount)
: base(Axes.Y) : base(ScrollingDirection.Up)
{ {
this.columnCount = columnCount; this.columnCount = columnCount;

View File

@ -16,13 +16,12 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
@ -98,8 +97,6 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f); protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this); protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
} }
} }

View File

@ -64,7 +64,6 @@
<Compile Include="Judgements\HoldNoteTickJudgement.cs" /> <Compile Include="Judgements\HoldNoteTickJudgement.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" /> <Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Mods\IGenerateSpeedAdjustments.cs" />
<Compile Include="Mods\ManiaKeyMod.cs" /> <Compile Include="Mods\ManiaKeyMod.cs" />
<Compile Include="Mods\ManiaModAutoplay.cs" /> <Compile Include="Mods\ManiaModAutoplay.cs" />
<Compile Include="Mods\ManiaModDaycore.cs" /> <Compile Include="Mods\ManiaModDaycore.cs" />
@ -114,8 +113,6 @@
<Compile Include="Tests\TestCaseManiaHitObjects.cs" /> <Compile Include="Tests\TestCaseManiaHitObjects.cs" />
<Compile Include="Tests\TestCaseManiaPlayfield.cs" /> <Compile Include="Tests\TestCaseManiaPlayfield.cs" />
<Compile Include="Tests\TestCasePerformancePoints.cs" /> <Compile Include="Tests\TestCasePerformancePoints.cs" />
<Compile Include="Timing\GravityScrollingContainer.cs" />
<Compile Include="Timing\ScrollingAlgorithm.cs" />
<Compile Include="UI\Column.cs" /> <Compile Include="UI\Column.cs" />
<Compile Include="UI\DrawableManiaJudgement.cs" /> <Compile Include="UI\DrawableManiaJudgement.cs" />
<Compile Include="UI\HitExplosion.cs" /> <Compile Include="UI\HitExplosion.cs" />
@ -123,9 +120,7 @@
<Compile Include="UI\ManiaPlayfield.cs" /> <Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" /> <Compile Include="ManiaRuleset.cs" />
<Compile Include="Mods\ManiaModNoFail.cs" /> <Compile Include="Mods\ManiaModNoFail.cs" />
<Compile Include="Mods\ManiaModGravity.cs" />
<Compile Include="UI\SpecialColumnPosition.cs" /> <Compile Include="UI\SpecialColumnPosition.cs" />
<Compile Include="Timing\ManiaSpeedAdjustmentContainer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Beatmaps namespace osu.Game.Rulesets.Osu.Beatmaps
{ {
@ -37,7 +36,6 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
private void applyStacking(Beatmap<OsuHitObject> beatmap) private void applyStacking(Beatmap<OsuHitObject> beatmap)
{ {
const int stack_distance = 3; const int stack_distance = 3;
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
// Reset stacking // Reset stacking
for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++) for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
@ -58,6 +56,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
continue; continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime; double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
if (objectN.StartTime - endTime > stackThreshold) if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object. //We are no longer within stacking range of the next object.
@ -100,6 +99,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
OsuHitObject objectI = beatmap.HitObjects[i]; OsuHitObject objectI = beatmap.HitObjects[i];
if (objectI.StackHeight != 0 || objectI is Spinner) continue; if (objectI.StackHeight != 0 || objectI is Spinner) continue;
float stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
/* If this object is a hitcircle, then we enter this "special" case. /* If this object is a hitcircle, then we enter this "special" case.
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider. * It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
* Any other case is handled by the "is Slider" code below this. * Any other case is handled by the "is Slider" code below this.

View File

@ -16,17 +16,15 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage."; public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
private const double fade_in_duration_multiplier = 0.4; private const float fade_in_duration_multiplier = 0.4f;
private const double fade_out_duration_multiplier = 0.3; private const double fade_out_duration_multiplier = 0.3;
private float preEmpt => DrawableOsuHitObject.TIME_PREEMPT;
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables) public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{ {
foreach (var d in drawables.OfType<DrawableOsuHitObject>()) foreach (var d in drawables.OfType<DrawableOsuHitObject>())
{ {
d.ApplyCustomUpdateState += ApplyHiddenState; d.ApplyCustomUpdateState += ApplyHiddenState;
d.FadeInDuration = preEmpt * fade_in_duration_multiplier; d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier;
} }
} }
@ -35,8 +33,8 @@ namespace osu.Game.Rulesets.Osu.Mods
if (!(drawable is DrawableOsuHitObject d)) if (!(drawable is DrawableOsuHitObject d))
return; return;
var fadeOutStartTime = d.HitObject.StartTime - preEmpt + d.FadeInDuration; var fadeOutStartTime = d.HitObject.StartTime - d.HitObject.TimePreempt + d.HitObject.TimeFadein;
var fadeOutDuration = preEmpt * fade_out_duration_multiplier; var fadeOutDuration = d.HitObject.TimePreempt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out) // new duration from completed fade in to end (before fading out)
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime; var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;

View File

@ -96,12 +96,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
using (fp.BeginAbsoluteSequence(fadeInTime)) using (fp.BeginAbsoluteSequence(fadeInTime))
{ {
fp.FadeIn(DrawableOsuHitObject.TIME_FADEIN); fp.FadeIn(currHitObject.TimeFadein);
fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, Easing.Out); fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out);
fp.MoveTo(pointEndPosition, DrawableOsuHitObject.TIME_FADEIN, Easing.Out); fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out);
fp.Delay(fadeOutTime - fadeInTime).FadeOut(DrawableOsuHitObject.TIME_FADEIN); fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein);
} }
fp.Expire(true); fp.Expire(true);

View File

@ -88,8 +88,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
base.UpdatePreemptState(); base.UpdatePreemptState();
ApproachCircle.FadeIn(Math.Min(FadeInDuration * 2, TIME_PREEMPT)); ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1.1f, TIME_PREEMPT); ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateCurrentState(ArmedState state)
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
switch (state) switch (state)
{ {
case ArmedState.Idle: case ArmedState.Idle:
this.Delay(TIME_PREEMPT).FadeOut(500); this.Delay(HitObject.TimePreempt).FadeOut(500);
Expire(true); Expire(true);

View File

@ -10,15 +10,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject> public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
{ {
public const float TIME_PREEMPT = 600; public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt;
public const float TIME_FADEIN = 400;
/// <summary>
/// The number of milliseconds used to fade in.
/// </summary>
public virtual double FadeInDuration { get; set; } = TIME_FADEIN;
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - TIME_PREEMPT;
protected DrawableOsuHitObject(OsuHitObject hitObject) protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject) : base(hitObject)
@ -29,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected sealed override void UpdateState(ArmedState state) protected sealed override void UpdateState(ArmedState state)
{ {
double transformTime = HitObject.StartTime - TIME_PREEMPT; double transformTime = HitObject.StartTime - HitObject.TimePreempt;
base.ApplyTransformsAt(transformTime, true); base.ApplyTransformsAt(transformTime, true);
base.ClearTransformsAfter(transformTime, true); base.ClearTransformsAfter(transformTime, true);
@ -38,12 +30,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
UpdatePreemptState(); UpdatePreemptState();
using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true)) using (BeginDelayedSequence(HitObject.TimePreempt + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
UpdateCurrentState(state); UpdateCurrentState(state);
} }
} }
protected virtual void UpdatePreemptState() => this.FadeIn(FadeInDuration); protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
protected virtual void UpdateCurrentState(ArmedState state) protected virtual void UpdateCurrentState(ArmedState state)
{ {

View File

@ -58,7 +58,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Scale = s.Scale, Scale = s.Scale,
ComboColour = s.ComboColour, ComboColour = s.ComboColour,
Samples = s.Samples, Samples = s.Samples,
SampleControlPoint = s.SampleControlPoint SampleControlPoint = s.SampleControlPoint,
TimePreempt = s.TimePreempt,
TimeFadein = s.TimeFadein
}) })
}; };
@ -71,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>()) foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
{ {
var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration; var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration;
var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2); var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
var fadeOutTime = repeatStartTime + repeatDuration; var fadeOutTime = repeatStartTime + repeatDuration;
var drawableTick = new DrawableSliderTick(tick) var drawableTick = new DrawableSliderTick(tick)
@ -88,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>()) foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>())
{ {
var repeatStartTime = s.StartTime + repeatPoint.RepeatIndex * repeatDuration; var repeatStartTime = s.StartTime + repeatPoint.RepeatIndex * repeatDuration;
var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2); var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
var fadeOutTime = repeatStartTime + repeatDuration; var fadeOutTime = repeatStartTime + repeatDuration;
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
@ -106,12 +108,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private int currentRepeat; private int currentRepeat;
public bool Tracking; public bool Tracking;
public override double FadeInDuration
{
get { return base.FadeInDuration; }
set { InitialCircle.FadeInDuration = base.FadeInDuration = value; }
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();

View File

@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton); Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
if (!spmCounter.IsPresent && Disc.Tracking) if (!spmCounter.IsPresent && Disc.Tracking)
spmCounter.FadeIn(FadeInDuration); spmCounter.FadeIn(HitObject.TimeFadein);
base.Update(); base.Update();
} }
@ -191,14 +191,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.UpdatePreemptState(); base.UpdatePreemptState();
circleContainer.ScaleTo(Spinner.Scale * 0.3f); circleContainer.ScaleTo(Spinner.Scale * 0.3f);
circleContainer.ScaleTo(Spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint); circleContainer.ScaleTo(Spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint);
Disc.RotateTo(-720); Disc.RotateTo(-720);
symbol.RotateTo(-720); symbol.RotateTo(-720);
mainContainer mainContainer
.ScaleTo(0) .ScaleTo(0)
.ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint) .ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, HitObject.TimePreempt - 150, Easing.OutQuint)
.Then() .Then()
.ScaleTo(1, 500, Easing.OutQuint); .ScaleTo(1, 500, Easing.OutQuint);
} }

View File

@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public void UpdateProgress(double progress, int repeat) public void UpdateProgress(double progress, int repeat)
{ {
double start = 0; double start = 0;
double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - DrawableOsuHitObject.TIME_PREEMPT)) / DrawableOsuHitObject.TIME_FADEIN, 0, 1) : 1; double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
if (repeat >= slider.RepeatCount - 1) if (repeat >= slider.RepeatCount - 1)
{ {

View File

@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects
private const double hit_window_100 = 80; private const double hit_window_100 = 80;
private const double hit_window_300 = 30; private const double hit_window_300 = 30;
public float TimePreempt = 600;
public float TimeFadein = 400;
public Vector2 Position { get; set; } public Vector2 Position { get; set; }
public float X => Position.X; public float X => Position.X;
public float Y => Position.Y; public float Y => Position.Y;
@ -72,6 +75,9 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
base.ApplyDefaultsToSelf(controlPointInfo, difficulty); base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
} }
} }

View File

@ -5,7 +5,6 @@ using OpenTK;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -133,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Replays
// Do some nice easing for cursor movements // Do some nice easing for cursor movements
if (Frames.Count > 0) if (Frames.Count > 0)
{ {
moveToHitObject(h.StartTime, startPosition, h.Radius, easing); moveToHitObject(h, startPosition, easing);
} }
// Add frames to click the hitobject // Add frames to click the hitobject
@ -191,12 +190,12 @@ namespace osu.Game.Rulesets.Osu.Replays
} }
} }
private void moveToHitObject(double targetTime, Vector2 targetPos, double hitObjectRadius, Easing easing) private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing)
{ {
ReplayFrame lastFrame = Frames[Frames.Count - 1]; ReplayFrame lastFrame = Frames[Frames.Count - 1];
// Wait until Auto could "see and react" to the next note. // Wait until Auto could "see and react" to the next note.
double waitTime = targetTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime); double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
if (waitTime > lastFrame.Time) if (waitTime > lastFrame.Time)
{ {
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState); lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
@ -205,17 +204,17 @@ namespace osu.Game.Rulesets.Osu.Replays
Vector2 lastPosition = lastFrame.Position; Vector2 lastPosition = lastFrame.Position;
double timeDifference = ApplyModsToTime(targetTime - lastFrame.Time); double timeDifference = ApplyModsToTime(h.StartTime - lastFrame.Time);
// Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up. // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up.
if (timeDifference > 0 && // Sanity checks if (timeDifference > 0 && // Sanity checks
((lastPosition - targetPos).Length > hitObjectRadius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough ((lastPosition - targetPos).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough
timeDifference >= 266)) // ... or the beats are slow enough to tap anyway. timeDifference >= 266)) // ... or the beats are slow enough to tap anyway.
{ {
// Perform eased movement // Perform eased movement
for (double time = lastFrame.Time + FrameDelay; time < targetTime; time += FrameDelay) for (double time = lastFrame.Time + FrameDelay; time < h.StartTime; time += FrameDelay)
{ {
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, targetTime, easing); Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPos, lastFrame.Time, h.StartTime, easing);
AddFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState)); AddFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
} }

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// <summary> /// <summary>
/// A line that scrolls alongside hit objects in the playfield and visualises control points. /// A line that scrolls alongside hit objects in the playfield and visualises control points.
/// </summary> /// </summary>
public class DrawableBarLine : DrawableScrollingHitObject<TaikoHitObject> public class DrawableBarLine : DrawableHitObject<TaikoHitObject>
{ {
/// <summary> /// <summary>
/// The width of the line tracker. /// The width of the line tracker.

View File

@ -34,15 +34,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRoll(DrumRoll drumRoll) public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll) : base(drumRoll)
{ {
Width = (float)HitObject.Duration; RelativeSizeAxes = Axes.Y;
Container<DrawableDrumRollTick> tickContainer; Container<DrawableDrumRollTick> tickContainer;
MainPiece.Add(tickContainer = new Container<DrawableDrumRollTick> MainPiece.Add(tickContainer = new Container<DrawableDrumRollTick> { RelativeSizeAxes = Axes.Both });
{
RelativeSizeAxes = Axes.Both,
RelativeChildOffset = new Vector2((float)HitObject.StartTime, 0),
RelativeChildSize = new Vector2((float)HitObject.Duration, 1)
});
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>()) foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
{ {
@ -100,6 +95,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
switch (state)
{
case ArmedState.Hit:
case ArmedState.Miss:
this.FadeOut(100).Expire();
break;
}
} }
} }
} }

View File

@ -15,23 +15,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRollTick(DrumRollTick tick) public DrawableDrumRollTick(DrumRollTick tick)
: base(tick) : base(tick)
{ {
// Because ticks aren't added by the ScrollingPlayfield, we need to set the following properties ourselves
RelativePositionAxes = Axes.X;
X = (float)tick.StartTime;
FillMode = FillMode.Fit; FillMode = FillMode.Fit;
} }
public override bool DisplayJudgement => false; public override bool DisplayJudgement => false;
protected override void LoadComplete()
{
base.LoadComplete();
// We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
Width *= Parent.RelativeChildSize.X;
}
protected override TaikoPiece CreateMainPiece() => new TickPiece protected override TaikoPiece CreateMainPiece() => new TickPiece
{ {
Filled = HitObject.FirstTick Filled = HitObject.FirstTick
@ -55,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (state) switch (state)
{ {
case ArmedState.Hit: case ArmedState.Hit:
Content.ScaleTo(0, 100, Easing.OutQuint); Content.ScaleTo(0, 100, Easing.OutQuint).Expire();
break; break;
} }
} }

View File

@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Size = BaseSize * Parent.RelativeChildSize; Size = BaseSize * Parent.RelativeChildSize;
// Make the swell stop at the hit target // Make the swell stop at the hit target
X = (float)Math.Max(Time.Current, HitObject.StartTime); X = Math.Max(0, X);
double t = Math.Min(HitObject.StartTime, Time.Current); double t = Math.Min(HitObject.StartTime, Time.Current);
if (t == HitObject.StartTime && !hasStarted) if (t == HitObject.StartTime && !hasStarted)

View File

@ -12,7 +12,7 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableScrollingHitObject<TaikoHitObject>, IKeyBindingHandler<TaikoAction> public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject>, IKeyBindingHandler<TaikoAction>
where TaikoHitType : TaikoHitObject where TaikoHitType : TaikoHitObject
{ {
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
@ -17,6 +16,7 @@ using System.Linq;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
{ {
@ -37,6 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary> /// </summary>
private const float left_area_size = 240; private const float left_area_size = 240;
protected override bool UserScrollSpeedAdjustment => false;
private readonly Container<HitExplosion> hitExplosionContainer; private readonly Container<HitExplosion> hitExplosionContainer;
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer; private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
@ -56,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Box background; private readonly Box background;
public TaikoPlayfield(ControlPointInfo controlPoints) public TaikoPlayfield(ControlPointInfo controlPoints)
: base(Axes.X) : base(ScrollingDirection.Left)
{ {
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
{ {

View File

@ -17,6 +17,7 @@ using osu.Game.Rulesets.Taiko.Replays;
using OpenTK; using OpenTK;
using System.Linq; using System.Linq;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation;
using OpenTK; using OpenTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -18,26 +19,10 @@ namespace osu.Game.Tests.Visual
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SelectionLayer) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SelectionLayer) };
public TestCaseEditorSelectionLayer() [BackgroundDependencyLoader]
private void load()
{ {
var playfield = new OsuEditPlayfield var playfield = new OsuEditPlayfield();
{
new DrawableHitCircle(new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }),
new DrawableHitCircle(new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }),
new DrawableSlider(new Slider
{
ControlPoints = new List<Vector2>
{
new Vector2(128, 256),
new Vector2(344, 256),
},
Distance = 400,
Position = new Vector2(128, 256),
Velocity = 1,
TickDistance = 100,
Scale = 0.5f
})
};
Children = new Drawable[] Children = new Drawable[]
{ {
@ -49,6 +34,22 @@ namespace osu.Game.Tests.Visual
}, },
new SelectionLayer(playfield) new SelectionLayer(playfield)
}; };
playfield.Add(new DrawableHitCircle(new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }));
playfield.Add(new DrawableHitCircle(new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }));
playfield.Add(new DrawableSlider(new Slider
{
ControlPoints = new List<Vector2>
{
new Vector2(128, 256),
new Vector2(344, 256),
},
Distance = 400,
Position = new Vector2(128, 256),
Velocity = 1,
TickDistance = 100,
Scale = 0.5f
}));
} }
} }
} }

View File

@ -0,0 +1,184 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Extensions.IEnumerableExtensions;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Tests.Visual
{
public class TestCaseScrollingHitObjects : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Playfield) };
private readonly TestPlayfield[] playfields = new TestPlayfield[4];
public TestCaseScrollingHitObjects()
{
Add(new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
playfields[0] = new TestPlayfield(ScrollingDirection.Up),
playfields[1] = new TestPlayfield(ScrollingDirection.Down)
},
new Drawable[]
{
playfields[2] = new TestPlayfield(ScrollingDirection.Left),
playfields[3] = new TestPlayfield(ScrollingDirection.Right)
}
}
});
AddSliderStep("Time range", 100, 10000, 5000, v => playfields.ForEach(p => p.VisibleTimeRange.Value = v));
AddStep("Add control point", () => addControlPoint(Time.Current + 5000));
}
protected override void LoadComplete()
{
base.LoadComplete();
playfields.ForEach(p => p.HitObjects.AddControlPoint(new MultiplierControlPoint(0)));
for (int i = 0; i <= 5000; i += 1000)
addHitObject(Time.Current + i);
Scheduler.AddDelayed(() => addHitObject(Time.Current + 5000), 1000, true);
}
private void addHitObject(double time)
{
playfields.ForEach(p =>
{
var hitObject = new TestDrawableHitObject(time);
setAnchor(hitObject, p);
p.Add(hitObject);
});
}
private void addControlPoint(double time)
{
playfields.ForEach(p =>
{
p.HitObjects.AddControlPoint(new MultiplierControlPoint(time) { DifficultyPoint = { SpeedMultiplier = 3 } });
p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 2000) { DifficultyPoint = { SpeedMultiplier = 2 } });
p.HitObjects.AddControlPoint(new MultiplierControlPoint(time + 3000) { DifficultyPoint = { SpeedMultiplier = 1 } });
TestDrawableControlPoint createDrawablePoint(double t)
{
var obj = new TestDrawableControlPoint(p.Direction, t);
setAnchor(obj, p);
return obj;
}
p.Add(createDrawablePoint(time));
p.Add(createDrawablePoint(time + 2000));
p.Add(createDrawablePoint(time + 3000));
});
}
private void setAnchor(DrawableHitObject obj, TestPlayfield playfield)
{
switch (playfield.Direction)
{
case ScrollingDirection.Up:
obj.Anchor = Anchor.TopCentre;
break;
case ScrollingDirection.Down:
obj.Anchor = Anchor.BottomCentre;
break;
case ScrollingDirection.Left:
obj.Anchor = Anchor.CentreLeft;
break;
case ScrollingDirection.Right:
obj.Anchor = Anchor.CentreRight;
break;
}
}
private class TestPlayfield : ScrollingPlayfield
{
public readonly ScrollingDirection Direction;
public TestPlayfield(ScrollingDirection direction)
: base(direction)
{
Direction = direction;
Padding = new MarginPadding(2);
ScaledContent.Masking = true;
AddInternal(new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f,
Depth = float.MaxValue
});
}
}
private class TestDrawableControlPoint : DrawableHitObject<HitObject>
{
public TestDrawableControlPoint(ScrollingDirection direction, double time)
: base(new HitObject { StartTime = time })
{
Origin = Anchor.Centre;
Add(new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both
});
switch (direction)
{
case ScrollingDirection.Up:
case ScrollingDirection.Down:
RelativeSizeAxes = Axes.X;
Height = 2;
break;
case ScrollingDirection.Left:
case ScrollingDirection.Right:
RelativeSizeAxes = Axes.Y;
Width = 2;
break;
}
}
protected override void UpdateState(ArmedState state)
{
}
}
private class TestDrawableHitObject : DrawableHitObject<HitObject>
{
public TestDrawableHitObject(double time)
: base(new HitObject { StartTime = time })
{
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
Add(new Box { Size = new Vector2(75) });
}
protected override void UpdateState(ArmedState state)
{
}
}
}
}

View File

@ -1,219 +0,0 @@
// Copyright (c) 2007-2018 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 NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI;
using osu.Game.Tests.Beatmaps;
using OpenTK;
namespace osu.Game.Tests.Visual
{
/// <summary>
/// The most minimal implementation of a playfield with scrolling hit objects.
/// </summary>
[TestFixture]
public class TestCaseScrollingPlayfield : OsuTestCase
{
public TestCaseScrollingPlayfield()
{
Clock = new FramedClock();
var objects = new List<HitObject>();
int time = 1500;
for (int i = 0; i < 50; i++)
{
objects.Add(new TestHitObject { StartTime = time });
time += 500;
}
Beatmap b = new Beatmap
{
HitObjects = objects,
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty(),
Metadata = new BeatmapMetadata()
}
};
WorkingBeatmap beatmap = new TestWorkingBeatmap(b);
TestRulesetContainer horizontalRulesetContainer;
Add(horizontalRulesetContainer = new TestRulesetContainer(Axes.X, beatmap, true));
TestRulesetContainer verticalRulesetContainer;
Add(verticalRulesetContainer = new TestRulesetContainer(Axes.Y, beatmap, true));
AddStep("Reverse direction", () =>
{
horizontalRulesetContainer.Playfield.Reverse();
verticalRulesetContainer.Playfield.Reverse();
});
}
[Test]
public void TestSpeedAdjustmentOrdering()
{
var hitObjectContainer = new ScrollingPlayfield.ScrollingHitObjectContainer(Axes.X);
var speedAdjustments = new[]
{
new SpeedAdjustmentContainer(new MultiplierControlPoint()),
new SpeedAdjustmentContainer(new MultiplierControlPoint(1000)
{
TimingPoint = new TimingControlPoint { BeatLength = 500 }
}),
new SpeedAdjustmentContainer(new MultiplierControlPoint(2000)
{
TimingPoint = new TimingControlPoint { BeatLength = 1000 },
DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 2}
}),
new SpeedAdjustmentContainer(new MultiplierControlPoint(3000)
{
TimingPoint = new TimingControlPoint { BeatLength = 1000 },
DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 1}
}),
};
var hitObjects = new[]
{
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = -1000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject()),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 1000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 2000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 3000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 4000 }),
};
hitObjects.ForEach(h => hitObjectContainer.Add(h));
speedAdjustments.ForEach(hitObjectContainer.AddSpeedAdjustment);
// The 0th index in hitObjectContainer.SpeedAdjustments is the "default" control point
// Check multiplier of the default speed adjustment
Assert.AreEqual(1, hitObjectContainer.SpeedAdjustments[0].ControlPoint.Multiplier);
Assert.AreEqual(1, speedAdjustments[0].ControlPoint.Multiplier);
Assert.AreEqual(2, speedAdjustments[1].ControlPoint.Multiplier);
Assert.AreEqual(2, speedAdjustments[2].ControlPoint.Multiplier);
Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
// Check insertion of hit objects
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[4].Contains(hitObjects[0]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[2].Contains(hitObjects[2]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[1].Contains(hitObjects[3]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[4]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[5]));
hitObjectContainer.RemoveSpeedAdjustment(hitObjectContainer.SpeedAdjustments[3]);
// The hit object contained in this speed adjustment should be resorted into the one occuring before it
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
}
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject>
{
private readonly Axes scrollingAxes;
public TestRulesetContainer(Axes scrollingAxes, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(null, beatmap, isForCurrentRuleset)
{
this.scrollingAxes = scrollingAxes;
}
public new TestPlayfield Playfield => base.Playfield;
public override ScoreProcessor CreateScoreProcessor() => new TestScoreProcessor();
public override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
protected override BeatmapConverter<TestHitObject> CreateBeatmapConverter() => new TestBeatmapConverter();
protected override Playfield CreatePlayfield() => new TestPlayfield(scrollingAxes);
protected override DrawableHitObject<TestHitObject> GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
}
private class TestScoreProcessor : ScoreProcessor<TestHitObject>
{
protected override void OnNewJudgement(Judgement judgement)
{
}
}
private class TestBeatmapConverter : BeatmapConverter<TestHitObject>
{
protected override IEnumerable<Type> ValidConversionTypes => new[] { typeof(HitObject) };
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{
yield return original as TestHitObject;
}
}
private class DrawableTestHitObject : DrawableScrollingHitObject<TestHitObject>
{
public DrawableTestHitObject(Axes scrollingAxes, TestHitObject hitObject)
: base(hitObject)
{
Anchor = scrollingAxes == Axes.Y ? Anchor.TopCentre : Anchor.CentreLeft;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
Add(new Circle
{
Size = new Vector2(50)
});
}
protected override void UpdateState(ArmedState state)
{
}
}
private class TestPlayfield : ScrollingPlayfield
{
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
public TestPlayfield(Axes scrollingAxes)
: base(scrollingAxes)
{
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f
},
content = new Container { RelativeSizeAxes = Axes.Both }
};
}
public void Reverse() => Reversed.Toggle();
}
private class TestHitObject : HitObject
{
}
}
}

View File

@ -1,13 +1,26 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Social;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
public class TestCaseSocial : OsuTestCase public class TestCaseSocial : OsuTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(UserPanel),
typeof(SocialPanel),
typeof(FilterControl),
typeof(SocialOverlay),
typeof(SocialGridPanel),
typeof(SocialListPanel)
};
public TestCaseSocial() public TestCaseSocial()
{ {
SocialOverlay s = new SocialOverlay SocialOverlay s = new SocialOverlay

View File

@ -141,7 +141,7 @@
<Compile Include="Visual\TestCaseResults.cs" /> <Compile Include="Visual\TestCaseResults.cs" />
<Compile Include="Visual\TestCaseRoomInspector.cs" /> <Compile Include="Visual\TestCaseRoomInspector.cs" />
<Compile Include="Visual\TestCaseScoreCounter.cs" /> <Compile Include="Visual\TestCaseScoreCounter.cs" />
<Compile Include="Visual\TestCaseScrollingPlayfield.cs" /> <Compile Include="Visual\TestCaseScrollingHitObjects.cs" />
<Compile Include="Visual\TestCaseSettings.cs" /> <Compile Include="Visual\TestCaseSettings.cs" />
<Compile Include="Visual\TestCaseSkipButton.cs" /> <Compile Include="Visual\TestCaseSkipButton.cs" />
<Compile Include="Visual\TestCaseSocial.cs" /> <Compile Include="Visual\TestCaseSocial.cs" />

View File

@ -566,7 +566,6 @@ namespace osu.Game.Beatmaps
using (var stream = new StreamReader(reader.GetStream(mapName))) using (var stream = new StreamReader(reader.GetStream(mapName)))
metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
// check if a set already exists with the same online id. // check if a set already exists with the same online id.
if (metadata.OnlineBeatmapSetID != null) if (metadata.OnlineBeatmapSetID != null)
beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID); beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID);
@ -581,7 +580,6 @@ namespace osu.Game.Beatmaps
Metadata = metadata Metadata = metadata
}; };
var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu")); var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu"));
foreach (var name in mapNames) foreach (var name in mapNames)
@ -693,7 +691,6 @@ namespace osu.Game.Beatmaps
{ {
try try
{ {
using (var beatmap = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) using (var beatmap = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
{ {
Decoder decoder = Decoder.GetDecoder(beatmap); Decoder decoder = Decoder.GetDecoder(beatmap);

View File

@ -27,7 +27,6 @@ namespace osu.Game.Beatmaps
Beatmap = CreateBeatmapConverter(beatmap).Convert(beatmap); Beatmap = CreateBeatmapConverter(beatmap).Convert(beatmap);
Mods = mods ?? new Mod[0]; Mods = mods ?? new Mod[0];
ApplyMods(Mods); ApplyMods(Mods);
PreprocessHitObjects(); PreprocessHitObjects();

View File

@ -137,7 +137,7 @@ namespace osu.Game.Beatmaps.Formats
CentreRight, CentreRight,
BottomLeft, BottomLeft,
BottomRight BottomRight
}; }
internal enum StoryLayer internal enum StoryLayer
{ {

View File

@ -72,6 +72,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.FloatingComments, false); Set(OsuSetting.FloatingComments, false);
Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential);
// Update // Update
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer); Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
@ -116,6 +118,7 @@ namespace osu.Game.Configuration
ShowFpsDisplay, ShowFpsDisplay,
ChatDisplayHeight, ChatDisplayHeight,
Version, Version,
ShowConvertedBeatmaps ShowConvertedBeatmaps,
SpeedChangeVisualisation
} }
} }

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Configuration
{
public enum SpeedChangeVisualisationMethod
{
[Description("Sequential")]
Sequential,
[Description("Overlapping")]
Overlapping
}
}

View File

@ -2,6 +2,7 @@
// 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.Globalization;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -17,7 +18,7 @@ using osu.Framework.Graphics.Shapes;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
{ {
public class OsuSliderBar<T> : SliderBar<T>, IHasTooltip, IHasAccentColour public class OsuSliderBar<T> : SliderBar<T>, IHasTooltip, IHasAccentColour
where T : struct, IEquatable<T> where T : struct, IEquatable<T>, IComparable, IConvertible
{ {
private SampleChannel sample; private SampleChannel sample;
private double lastSampleTime; private double lastSampleTime;
@ -32,18 +33,25 @@ namespace osu.Game.Graphics.UserInterface
get get
{ {
var bindableDouble = CurrentNumber as BindableNumber<double>; var bindableDouble = CurrentNumber as BindableNumber<double>;
if (bindableDouble != null) var bindableFloat = CurrentNumber as BindableNumber<float>;
var floatValue = bindableDouble?.Value ?? bindableFloat?.Value;
if (floatValue != null)
{ {
if (bindableDouble.MaxValue == 1 && (bindableDouble.MinValue == 0 || bindableDouble.MinValue == -1)) var floatMinValue = bindableDouble?.MinValue ?? bindableFloat.MinValue;
return bindableDouble.Value.ToString(@"P0"); var floatMaxValue = bindableDouble?.MaxValue ?? bindableFloat.MaxValue;
return bindableDouble.Value.ToString(@"n1");
if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1))
return floatValue.Value.ToString("P0");
return floatValue.Value.ToString("N1");
} }
var bindableInt = CurrentNumber as BindableNumber<int>; var bindableInt = CurrentNumber as BindableNumber<int>;
if (bindableInt != null) if (bindableInt != null)
return bindableInt.Value.ToString(@"n0"); return bindableInt.Value.ToString("N0");
return Current.Value.ToString(); return Current.Value.ToString(CultureInfo.InvariantCulture);
} }
} }

View File

@ -180,7 +180,6 @@ namespace osu.Game.Graphics.UserInterface
} }
} }
protected class OsuTabDropdownHeader : OsuDropdownHeader protected class OsuTabDropdownHeader : OsuDropdownHeader
{ {
public override Color4 AccentColour public override Color4 AccentColour

View File

@ -56,7 +56,6 @@ namespace osu.Game.Online.API
} }
catch catch
{ {
} }
return null; return null;
} }

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests
{
public class GetFriendsRequest : APIRequest<List<User>>
{
protected override string Target => @"friends";
}
}

View File

@ -219,7 +219,6 @@ namespace osu.Game.Overlays.Chat
} }
else else
contentFlow.Text = message.Content; contentFlow.Text = message.Content;
} }
private class MessageSender : OsuClickableContainer, IHasContextMenu private class MessageSender : OsuClickableContainer, IHasContextMenu

View File

@ -65,7 +65,6 @@ namespace osu.Game.Overlays.Direct
Colour = Color4.Black.Opacity(0.3f), Colour = Color4.Black.Opacity(0.3f),
}; };
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay) private void load(BeatmapManager beatmaps, OsuColour colours, BeatmapSetOverlay beatmapSetOverlay)
{ {

View File

@ -174,10 +174,7 @@ namespace osu.Game.Overlays.Direct
private void load(AudioManager audio) private void load(AudioManager audio)
{ {
if (!string.IsNullOrEmpty(preview)) if (!string.IsNullOrEmpty(preview))
{
Preview = audio.Track.Get(preview); Preview = audio.Track.Get(preview);
Preview.Volume.Value = 0.5;
}
} }
} }
} }

View File

@ -183,8 +183,6 @@ namespace osu.Game.Overlays.MedalSplash
description.FadeInFromZero(duration * 2); description.FadeInFromZero(duration * 2);
break; break;
} }
} }
} }

View File

@ -148,7 +148,6 @@ namespace osu.Game.Overlays.Music
private class PlaylistItemHandle : SpriteIcon private class PlaylistItemHandle : SpriteIcon
{ {
public PlaylistItemHandle() public PlaylistItemHandle()
{ {
Anchor = Anchor.TopLeft; Anchor = Anchor.TopLeft;

View File

@ -230,7 +230,6 @@ namespace osu.Game.Overlays.Music
items.ChangeChildDepth(draggedItem, dstIndex); items.ChangeChildDepth(draggedItem, dstIndex);
} }
private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren private class ItemSearchContainer : FillFlowContainer<PlaylistItem>, IHasFilterableChildren
{ {
public IEnumerable<string> FilterTerms => new string[] { }; public IEnumerable<string> FilterTerms => new string[] { };

View File

@ -168,5 +168,4 @@ namespace osu.Game.Overlays.Notifications
// the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297). // the layout portion of this is being tracked as a framework issue (https://github.com/ppy/osu-framework/issues/1297).
protected override bool RequiresChildrenUpdate => true; protected override bool RequiresChildrenUpdate => true;
} }
} }

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
namespace osu.Game.Overlays.Notifications namespace osu.Game.Overlays.Notifications
{ {
public class ProgressCompletionNotification : SimpleNotification public class ProgressCompletionNotification : SimpleNotification

View File

@ -217,7 +217,6 @@ namespace osu.Game.Overlays.Notifications
}; };
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {

View File

@ -0,0 +1,26 @@
// Copyright (c) 2007-2018 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.Game.Configuration;
namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
public class ScrollingSettings : SettingsSubsection
{
protected override string Header => "Scrolling";
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
Children = new[]
{
new SettingsEnumDropdown<SpeedChangeVisualisationMethod>
{
LabelText = "Visualise speed changes as",
Bindable = config.GetBindable<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation),
}
};
}
}
}

View File

@ -21,6 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections
{ {
new GeneralSettings(), new GeneralSettings(),
new SongSelectSettings(), new SongSelectSettings(),
new ScrollingSettings()
}; };
} }

View File

@ -4,19 +4,18 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings namespace osu.Game.Overlays.Settings
{ {
public class SettingsSlider<T> : SettingsSlider<T, OsuSliderBar<T>> public class SettingsSlider<T> : SettingsSlider<T, OsuSliderBar<T>>
where T : struct, IEquatable<T> where T : struct, IEquatable<T>, IComparable, IConvertible
{ {
} }
public class SettingsSlider<T, U> : SettingsItem<T> public class SettingsSlider<T, U> : SettingsItem<T>
where T : struct, IEquatable<T> where T : struct, IEquatable<T>, IComparable, IConvertible
where U : SliderBar<T>, new() where U : OsuSliderBar<T>, new()
{ {
protected override Drawable CreateControl() => new U protected override Drawable CreateControl() => new U
{ {

View File

@ -22,7 +22,8 @@ namespace osu.Game.Overlays.Social
public enum SocialSortCriteria public enum SocialSortCriteria
{ {
Rank, Rank,
//Location, Name,
Location,
//[Description("Time Zone")] //[Description("Time Zone")]
//TimeZone, //TimeZone,
//[Description("World Map")] //[Description("World Map")]

View File

@ -18,7 +18,8 @@ namespace osu.Game.Overlays.Social
protected override Color4 BackgroundColour => OsuColour.FromHex(@"38202e"); protected override Color4 BackgroundColour => OsuColour.FromHex(@"38202e");
protected override float TabStripWidth => 438; protected override float TabStripWidth => 438;
protected override SocialTab DefaultTab => SocialTab.OnlinePlayers;
protected override SocialTab DefaultTab => SocialTab.AllPlayers;
protected override FontAwesome Icon => FontAwesome.fa_users; protected override FontAwesome Icon => FontAwesome.fa_users;
protected override Drawable CreateHeaderText() protected override Drawable CreateHeaderText()
@ -53,12 +54,12 @@ namespace osu.Game.Overlays.Social
public enum SocialTab public enum SocialTab
{ {
[Description("Online Players")] [Description("All Players")]
OnlinePlayers, AllPlayers,
//[Description("Online Friends")] [Description("Friends")]
//OnlineFriends, Friends,
//[Description("Online Team Members")] //[Description("Team Members")]
//OnlineTeamMembers, //TeamMembers,
//[Description("Chat Channels")] //[Description("Chat Channels")]
//ChatChannels, //ChatChannels,
} }

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Users;
namespace osu.Game.Overlays.Social
{
public class SocialGridPanel : SocialPanel
{
public SocialGridPanel(User user) : base(user)
{
Width = 300;
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) 2007-2018 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.Game.Users;
namespace osu.Game.Overlays.Social
{
public class SocialListPanel : SocialPanel
{
public SocialListPanel(User user) : base(user)
{
RelativeSizeAxes = Axes.X;
}
}
}

View File

@ -0,0 +1,60 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Users;
namespace osu.Game.Overlays.Social
{
public class SocialPanel : UserPanel
{
private const double hover_transition_time = 400;
public SocialPanel(User user) : base(user)
{
}
private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 2f,
Colour = Color4.Black.Opacity(0.25f),
};
private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 5f),
Radius = 10f,
Colour = Color4.Black.Opacity(0.3f),
};
protected override bool OnHover(InputState state)
{
Content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
Content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
Content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
Content.MoveToY(0, hover_transition_time, Easing.OutQuint);
base.OnHoverLost(state);
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(200, Easing.Out);
}
}
}

View File

@ -9,19 +9,22 @@ using OpenTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Overlays.SearchableList; using osu.Game.Overlays.SearchableList;
using osu.Game.Overlays.Social; using osu.Game.Overlays.Social;
using osu.Game.Users; using osu.Game.Users;
using osu.Framework.Configuration;
using osu.Framework.Threading;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public class SocialOverlay : SearchableListOverlay<SocialTab, SocialSortCriteria, SortDirection>, IOnlineComponent public class SocialOverlay : SearchableListOverlay<SocialTab, SocialSortCriteria, SortDirection>, IOnlineComponent
{ {
private readonly FillFlowContainer<UserPanel> panelFlow; private APIAccess api;
private readonly LoadingAnimation loading;
private FillFlowContainer<SocialPanel> panels;
protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b"); protected override Color4 BackgroundColour => OsuColour.FromHex(@"60284b");
protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"672b51"); protected override Color4 TrianglesColourLight => OsuColour.FromHex(@"672b51");
@ -31,27 +34,15 @@ namespace osu.Game.Overlays
protected override SearchableListFilterControl<SocialSortCriteria, SortDirection> CreateFilterControl() => new FilterControl(); protected override SearchableListFilterControl<SocialSortCriteria, SortDirection> CreateFilterControl() => new FilterControl();
private IEnumerable<User> users; private IEnumerable<User> users;
private readonly LoadingAnimation loading;
public IEnumerable<User> Users public IEnumerable<User> Users
{ {
get { return users; } get { return users; }
set set
{ {
if (users?.Equals(value) ?? false) return; if (users?.Equals(value) ?? false)
users = value; return;
if (users == null) users = value?.ToList();
panelFlow.Clear();
else
{
panelFlow.ChildrenEnumerable = users.Select(u =>
{
var p = new UserPanel(u) { Width = 300 };
p.Status.BindTo(u.Status);
return p;
});
}
} }
} }
@ -62,57 +53,153 @@ namespace osu.Game.Overlays
ThirdWaveColour = OsuColour.FromHex(@"9b2b6e"); ThirdWaveColour = OsuColour.FromHex(@"9b2b6e");
FourthWaveColour = OsuColour.FromHex(@"6d214d"); FourthWaveColour = OsuColour.FromHex(@"6d214d");
ScrollFlow.Children = new[] Add(loading = new LoadingAnimation());
Filter.Search.Current.ValueChanged += text =>
{ {
new OsuContextMenuContainer if (!string.IsNullOrEmpty(text))
{ {
RelativeSizeAxes = Axes.X, // force searching in players until searching for friends is supported
AutoSizeAxes = Axes.Y, Header.Tabs.Current.Value = SocialTab.AllPlayers;
Child = panelFlow = new FillFlowContainer<UserPanel>
{ if (Filter.Tabs.Current.Value != SocialSortCriteria.Rank)
RelativeSizeAxes = Axes.X, Filter.Tabs.Current.Value = SocialSortCriteria.Rank;
AutoSizeAxes = Axes.Y, }
Margin = new MarginPadding { Top = 20 },
Spacing = new Vector2(10f),
}
},
}; };
Add(loading = new LoadingAnimation()); Header.Tabs.Current.ValueChanged += tab => Scheduler.AddOnce(updateSearch);
Filter.Tabs.Current.ValueChanged += sortCriteria => Scheduler.AddOnce(updateSearch);
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels;
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += sortOrder => Scheduler.AddOnce(updateSearch);
currentQuery.ValueChanged += query =>
{
queryChangedDebounce?.Cancel();
if (string.IsNullOrEmpty(query))
Scheduler.AddOnce(updateSearch);
else
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500);
};
currentQuery.BindTo(Filter.Search.Current);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(APIAccess api) private void load(APIAccess api)
{ {
if (Users == null) this.api = api;
reloadUsers(api); api.Register(this);
} }
private void reloadUsers(APIAccess api) private void recreatePanels(PanelDisplayStyle displayStyle)
{ {
Users = null; clearPanels();
// no this is not the correct data source, but it's something. if (Users == null)
var request = new GetUsersRequest(); return;
request.Success += res =>
var newPanels = new FillFlowContainer<SocialPanel>
{ {
Users = res.Select(e => e.User); RelativeSizeAxes = Axes.X,
loading.Hide(); AutoSizeAxes = Axes.Y,
Spacing = new Vector2(10f),
Margin = new MarginPadding { Top = 10 },
ChildrenEnumerable = Users.Select(u =>
{
SocialPanel panel;
switch (displayStyle)
{
case PanelDisplayStyle.Grid:
panel = new SocialGridPanel(u)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
};
break;
default:
panel = new SocialListPanel(u);
break;
}
panel.Status.BindTo(u.Status);
return panel;
})
}; };
api.Queue(request); LoadComponentAsync(newPanels, f =>
{
if(panels != null)
ScrollFlow.Remove(panels);
ScrollFlow.Add(panels = newPanels);
});
}
private APIRequest getUsersRequest;
private readonly Bindable<string> currentQuery = new Bindable<string>();
private ScheduledDelegate queryChangedDebounce;
private void updateSearch()
{
queryChangedDebounce?.Cancel();
if (!IsLoaded)
return;
Users = null;
clearPanels();
loading.Hide();
getUsersRequest?.Cancel();
if (api?.IsLoggedIn != true)
return;
switch (Header.Tabs.Current.Value)
{
case SocialTab.Friends:
var friendRequest = new GetFriendsRequest(); // TODO filter arguments?
friendRequest.Success += updateUsers;
api.Queue(getUsersRequest = friendRequest);
break;
default:
var userRequest = new GetUsersRequest(); // TODO filter arguments!
userRequest.Success += response => updateUsers(response.Select(r => r.User));
api.Queue(getUsersRequest = userRequest);
break;
}
loading.Show(); loading.Show();
} }
private void updateUsers(IEnumerable<User> newUsers)
{
Users = newUsers;
loading.Hide();
recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value);
}
private void clearPanels()
{
if (panels != null)
{
panels.Expire();
panels = null;
}
}
public void APIStateChanged(APIAccess api, APIState state) public void APIStateChanged(APIAccess api, APIState state)
{ {
switch (state) switch (state)
{ {
case APIState.Online: case APIState.Online:
reloadUsers(api); Scheduler.AddOnce(updateSearch);
break; break;
default: default:
Users = null; Users = null;
clearPanels();
break; break;
} }
} }
@ -120,7 +207,7 @@ namespace osu.Game.Overlays
public enum SortDirection public enum SortDirection
{ {
Descending,
Ascending, Ascending,
Descending
} }
} }

View File

@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mods
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>. /// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>.
/// </summary> /// </summary>
/// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam> /// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam>
public interface IApplicableToBeatmapConverter<TObject> public interface IApplicableToBeatmapConverter<TObject> : IApplicableMod
where TObject : HitObject where TObject : HitObject
{ {
/// <summary> /// <summary>

View File

@ -5,30 +5,30 @@ using System;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
{ {
public abstract class ModAutoplay<T> : ModAutoplay, IApplicableToRulesetContainer<T> public class ModAutoplay<T> : ModAutoplay, IApplicableToRulesetContainer<T>
where T : HitObject where T : HitObject
{ {
protected abstract Score CreateReplayScore(Beatmap<T> beatmap); protected virtual Score CreateReplayScore(Beatmap<T> beatmap) => new Score { Replay = new Replay() };
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer) public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
{
rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay); public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer) => rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay);
}
} }
public class ModAutoplay : Mod, IApplicableFailOverride public abstract class ModAutoplay : Mod, IApplicableFailOverride
{ {
public override string Name => "Autoplay"; public override string Name => "Autoplay";
public override string ShortenedName => "AT"; public override string ShortenedName => "AT";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto; public override FontAwesome Icon => FontAwesome.fa_osu_mod_auto;
public override string Description => "Watch a perfect automated play through the song"; public override string Description => "Watch a perfect automated play through the song";
public override double ScoreMultiplier => 0; public override double ScoreMultiplier => 0;
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
public bool AllowFail => false; public bool AllowFail => false;
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
} }
} }

View File

@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Mods
{ {
public override string Name => "Cinema"; public override string Name => "Cinema";
public override string ShortenedName => "CN"; public override string ShortenedName => "CN";
public override bool HasImplementation => false;
public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema; public override FontAwesome Icon => FontAwesome.fa_osu_mod_cinema;
} }
} }

View File

@ -29,59 +29,54 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary> /// </summary>
public virtual Color4 AccentColour { get; set; } = Color4.Gray; public virtual Color4 AccentColour { get; set; } = Color4.Gray;
// Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first
protected virtual string SampleNamespace => null;
protected List<SampleChannel> Samples = new List<SampleChannel>();
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples;
private List<DrawableHitObject> nestedHitObjects;
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects;
public event Action<DrawableHitObject, Judgement> OnJudgement;
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
public IReadOnlyList<Judgement> Judgements => judgements;
private readonly List<Judgement> judgements = new List<Judgement>();
/// <summary> /// <summary>
/// Whether a visible judgement should be displayed when this representation is hit. /// Whether a visible judgement should be displayed when this representation is hit.
/// </summary> /// </summary>
public virtual bool DisplayJudgement => true; public virtual bool DisplayJudgement => true;
public override bool RemoveCompletedTransforms => false; /// <summary>
public override bool RemoveWhenNotAlive => false; /// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been hit.
/// </summary>
protected DrawableHitObject(HitObject hitObject) public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && (NestedHitObjects?.All(n => n.IsHit) ?? true);
{
HitObject = hitObject;
}
/// <summary> /// <summary>
/// The screen-space point that causes this <see cref="DrawableHitObject"/> to be selected in the Editor. /// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
/// </summary> /// </summary>
public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre; public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
/// <summary>
/// The screen-space quad that outlines this <see cref="DrawableHitObject"/> for selections in the Editor.
/// </summary>
public virtual Quad SelectionQuad => ScreenSpaceDrawQuad;
}
public abstract class DrawableHitObject<TObject> : DrawableHitObject
where TObject : HitObject
{
public event Action<DrawableHitObject, Judgement> OnJudgement;
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
public new readonly TObject HitObject;
public override bool HandleInput => Interactive;
public bool Interactive = true;
/// <summary> /// <summary>
/// Whether this <see cref="DrawableHitObject"/> can be judged. /// Whether this <see cref="DrawableHitObject"/> can be judged.
/// </summary> /// </summary>
protected virtual bool ProvidesJudgement => true; protected virtual bool ProvidesJudgement => true;
private readonly List<Judgement> judgements = new List<Judgement>(); private bool judgementOccurred;
public IReadOnlyList<Judgement> Judgements => judgements; private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
protected List<SampleChannel> Samples = new List<SampleChannel>(); public bool Interactive = true;
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples; public override bool HandleInput => Interactive;
// Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first public override bool RemoveWhenNotAlive => false;
protected virtual string SampleNamespace => null; public override bool RemoveCompletedTransforms => false;
protected override bool RequiresChildrenUpdate => true;
public readonly Bindable<ArmedState> State = new Bindable<ArmedState>(); public readonly Bindable<ArmedState> State = new Bindable<ArmedState>();
protected DrawableHitObject(TObject hitObject) protected DrawableHitObject(HitObject hitObject)
: base(hitObject)
{ {
HitObject = hitObject; HitObject = hitObject;
} }
@ -133,18 +128,47 @@ namespace osu.Game.Rulesets.Objects.Drawables
State.TriggerChange(); State.TriggerChange();
} }
protected void PlaySamples() protected abstract void UpdateState(ArmedState state);
{
Samples.ForEach(s => s?.Play());
}
private bool judgementOccurred;
private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
/// <summary> /// <summary>
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged. /// Bind to apply a custom state which can override the default implementation.
/// </summary> /// </summary>
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
protected void PlaySamples() => Samples.ForEach(s => s?.Play());
protected override void Update()
{
base.Update();
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
while (judgements.Count > 0)
{
var lastJudgement = judgements[judgements.Count - 1];
if (lastJudgement.TimeOffset + endTime <= Time.Current)
break;
judgements.RemoveAt(judgements.Count - 1);
State.Value = ArmedState.Idle;
OnJudgementRemoved?.Invoke(this, lastJudgement);
}
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
UpdateJudgement(false);
}
protected virtual void AddNested(DrawableHitObject h)
{
if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject>();
nestedHitObjects.Add(h);
}
/// <summary> /// <summary>
/// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>. /// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>.
@ -209,53 +233,30 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <param name="userTriggered">Whether the user triggered this check.</param> /// <param name="userTriggered">Whether the user triggered this check.</param>
/// <param name="timeOffset">The offset from the <see cref="HitObject"/> end time at which this check occurred. A <paramref name="timeOffset"/> &gt; 0 /// <param name="timeOffset">The offset from the <see cref="HitObject"/> end time at which this check occurred. A <paramref name="timeOffset"/> &gt; 0
/// implies that this check occurred after the end time of <see cref="HitObject"/>. </param> /// implies that this check occurred after the end time of <see cref="HitObject"/>. </param>
protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { } protected virtual void CheckForJudgements(bool userTriggered, double timeOffset)
protected override void Update()
{ {
base.Update();
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
while (judgements.Count > 0)
{
var lastJudgement = judgements[judgements.Count - 1];
if (lastJudgement.TimeOffset + endTime <= Time.Current)
break;
judgements.RemoveAt(judgements.Count - 1);
State.Value = ArmedState.Idle;
OnJudgementRemoved?.Invoke(this, lastJudgement);
}
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
UpdateJudgement(false);
}
private List<DrawableHitObject<TObject>> nestedHitObjects;
protected IEnumerable<DrawableHitObject<TObject>> NestedHitObjects => nestedHitObjects;
protected virtual void AddNested(DrawableHitObject<TObject> h)
{
if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject<TObject>>();
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
h.ApplyCustomUpdateState += (d, s) => ApplyCustomUpdateState?.Invoke(d, s);
nestedHitObjects.Add(h);
} }
/// <summary> /// <summary>
/// Bind to apply a custom state which can override the default implementation. /// The screen-space point that causes this <see cref="DrawableHitObject"/> to be selected in the Editor.
/// </summary> /// </summary>
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState; public virtual Vector2 SelectionPoint => ScreenSpaceDrawQuad.Centre;
protected abstract void UpdateState(ArmedState state); /// <summary>
/// The screen-space quad that outlines this <see cref="DrawableHitObject"/> for selections in the Editor.
/// </summary>
public virtual Quad SelectionQuad => ScreenSpaceDrawQuad;
}
public abstract class DrawableHitObject<TObject> : DrawableHitObject
where TObject : HitObject
{
public new readonly TObject HitObject;
protected DrawableHitObject(TObject hitObject)
: base(hitObject)
{
HitObject = hitObject;
}
} }
} }

View File

@ -1,67 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects.Drawables
{
/// <summary>
/// A basic class that overrides <see cref="DrawableHitObject{TObject}"/> and implements <see cref="IScrollingHitObject"/>.
/// This object does not need to have its <see cref="Drawable.RelativePositionAxes"/> set to be able to scroll, as this will
/// will be set by the scrolling container that contains it.
/// </summary>
public abstract class DrawableScrollingHitObject<TObject> : DrawableHitObject<TObject>, IScrollingHitObject
where TObject : HitObject
{
public BindableDouble LifetimeOffset { get; } = new BindableDouble();
Axes IScrollingHitObject.ScrollingAxes
{
set
{
RelativePositionAxes |= value;
if ((value & Axes.X) > 0)
X = (float)HitObject.StartTime;
if ((value & Axes.Y) > 0)
Y = (float)HitObject.StartTime;
}
}
public override bool RemoveWhenNotAlive => false;
protected override bool RequiresChildrenUpdate => true;
protected DrawableScrollingHitObject(TObject hitObject)
: base(hitObject)
{
}
private double? lifetimeStart;
public override double LifetimeStart
{
get { return lifetimeStart ?? HitObject.StartTime - LifetimeOffset; }
set { lifetimeStart = value; }
}
private double? lifetimeEnd;
public override double LifetimeEnd
{
get
{
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
return lifetimeEnd ?? endTime + LifetimeOffset;
}
set { lifetimeEnd = value; }
}
protected override void AddNested(DrawableHitObject<TObject> h)
{
var scrollingHitObject = h as IScrollingHitObject;
scrollingHitObject?.LifetimeOffset.BindTo(LifetimeOffset);
base.AddNested(h);
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Objects.Types namespace osu.Game.Rulesets.Objects.Types
{ {
/// <summary> /// <summary>

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Objects.Types namespace osu.Game.Rulesets.Objects.Types
{ {
/// <summary> /// <summary>

View File

@ -52,7 +52,6 @@ namespace osu.Game.Rulesets.Replays
protected ReplayFrame() protected ReplayFrame()
{ {
} }
public ReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) public ReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState)

View File

@ -1,28 +0,0 @@
// Copyright (c) 2007-2018 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.Rulesets.Timing
{
/// <summary>
/// A <see cref="ScrollingContainer"/> which scrolls linearly relative to the <see cref="MultiplierControlPoint"/> start time.
/// </summary>
public class LinearScrollingContainer : ScrollingContainer
{
private readonly MultiplierControlPoint controlPoint;
public LinearScrollingContainer(MultiplierControlPoint controlPoint)
{
this.controlPoint = controlPoint;
}
protected override void Update()
{
base.Update();
if ((ScrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current);
if ((ScrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current);
}
}
}

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Timing
/// <summary> /// <summary>
/// The time in milliseconds at which this <see cref="MultiplierControlPoint"/> starts. /// The time in milliseconds at which this <see cref="MultiplierControlPoint"/> starts.
/// </summary> /// </summary>
public readonly double StartTime; public double StartTime;
/// <summary> /// <summary>
/// The multiplier which this <see cref="MultiplierControlPoint"/> provides. /// The multiplier which this <see cref="MultiplierControlPoint"/> provides.

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