mirror of
https://github.com/ppy/osu.git
synced 2025-02-22 05:23:05 +08:00
Merge branch 'master' into url-parsing-support
This commit is contained in:
commit
45301d07fd
@ -1 +1 @@
|
|||||||
Subproject commit 80bcb82ef8d2e1af1ce077f4a037b6d279ad9e74
|
Subproject commit 49b563e2cf170eb19006b98dd5b69c2398362d9e
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
if (beatmap.ComboColors.Count == 0)
|
if (beatmap.ComboColors.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int comboIndex = 0;
|
int index = 0;
|
||||||
int colourIndex = 0;
|
int colourIndex = 0;
|
||||||
|
|
||||||
CatchHitObject lastObj = null;
|
CatchHitObject lastObj = null;
|
||||||
@ -31,12 +31,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
if (obj.NewCombo)
|
if (obj.NewCombo)
|
||||||
{
|
{
|
||||||
if (lastObj != null) lastObj.LastInCombo = true;
|
if (lastObj != null) lastObj.LastInCombo = true;
|
||||||
|
|
||||||
comboIndex = 0;
|
|
||||||
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
|
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.ComboIndex = comboIndex++;
|
obj.IndexInBeatmap = index++;
|
||||||
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
||||||
|
|
||||||
lastObj = obj;
|
lastObj = obj;
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch
|
namespace osu.Game.Rulesets.Catch
|
||||||
{
|
{
|
||||||
@ -80,7 +81,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
{
|
{
|
||||||
Mods = new Mod[]
|
Mods = new Mod[]
|
||||||
{
|
{
|
||||||
new ModAutoplay(),
|
new ModAutoplay<CatchHitObject>(),
|
||||||
new ModCinema(),
|
new ModCinema(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -101,7 +102,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
|
|
||||||
public override int LegacyID => 2;
|
public override int LegacyID => 2;
|
||||||
|
|
||||||
public CatchRuleset(RulesetInfo rulesetInfo)
|
public CatchRuleset(RulesetInfo rulesetInfo = null)
|
||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public float X { get; set; }
|
public float X { get; set; }
|
||||||
|
|
||||||
public Color4 ComboColour { get; set; } = Color4.Gray;
|
public Color4 ComboColour { get; set; }
|
||||||
public int ComboIndex { get; set; }
|
|
||||||
|
public int IndexInBeatmap { get; set; }
|
||||||
|
|
||||||
|
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
||||||
|
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
|
|
||||||
@ -44,4 +47,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
|
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum FruitVisualRepresentation
|
||||||
|
{
|
||||||
|
Pear,
|
||||||
|
Grape,
|
||||||
|
Raspberry,
|
||||||
|
Pineapple,
|
||||||
|
Banana // banananananannaanana
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,17 @@ 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
|
||||||
|
{
|
||||||
|
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,19 +30,17 @@ 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>
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
@ -8,15 +8,13 @@ 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)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
|
||||||
Size = new Vector2(Pulp.PULP_SIZE);
|
|
||||||
|
|
||||||
AccentColour = h.ComboColour;
|
AccentColour = h.ComboColour;
|
||||||
Masking = false;
|
Masking = false;
|
||||||
}
|
}
|
||||||
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
Child = new Pulp
|
Child = new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Scale = new Vector2(0.8f),
|
Size = Size
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
// 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.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
@ -11,14 +14,16 @@ 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;
|
||||||
|
|
||||||
public DrawableFruit(Fruit h)
|
public DrawableFruit(Fruit h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Size = new Vector2(Pulp.PULP_SIZE * 2.2f, Pulp.PULP_SIZE * 2.8f);
|
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
|
||||||
AccentColour = HitObject.ComboColour;
|
AccentColour = HitObject.ComboColour;
|
||||||
Masking = false;
|
Masking = false;
|
||||||
|
|
||||||
@ -28,48 +33,34 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
//todo: share this more
|
createPulp(HitObject.VisualRepresentation),
|
||||||
new BufferedContainer
|
border = new Circle
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
EdgeEffect = new EdgeEffectParameters
|
||||||
CacheDrawnFrameBuffer = true,
|
{
|
||||||
|
Hollow = !HitObject.HyperDash,
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Radius = 4,
|
||||||
|
Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f)
|
||||||
|
},
|
||||||
|
Size = new Vector2(Height * 1.5f),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
BorderColour = Color4.White,
|
||||||
|
BorderThickness = 4f,
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new Framework.Graphics.Drawable[]
|
||||||
{
|
{
|
||||||
new Pulp
|
new Box
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.Both,
|
AlwaysPresent = true,
|
||||||
Anchor = Anchor.TopCentre,
|
Colour = AccentColour,
|
||||||
Origin = Anchor.TopCentre,
|
Alpha = 0,
|
||||||
AccentColour = AccentColour,
|
RelativeSizeAxes = Axes.Both
|
||||||
Scale = new Vector2(0.6f),
|
}
|
||||||
},
|
|
||||||
new Pulp
|
|
||||||
{
|
|
||||||
RelativePositionAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft,
|
|
||||||
AccentColour = AccentColour,
|
|
||||||
Y = -0.08f
|
|
||||||
},
|
|
||||||
new Pulp
|
|
||||||
{
|
|
||||||
RelativePositionAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.CentreRight,
|
|
||||||
Origin = Anchor.CentreRight,
|
|
||||||
AccentColour = AccentColour,
|
|
||||||
Y = -0.08f
|
|
||||||
},
|
|
||||||
new Pulp
|
|
||||||
{
|
|
||||||
RelativePositionAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
AccentColour = AccentColour,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (HitObject.HyperDash)
|
if (HitObject.HyperDash)
|
||||||
@ -82,9 +73,184 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
AccentColour = Color4.Red,
|
AccentColour = Color4.Red,
|
||||||
Blending = BlendingMode.Additive,
|
Blending = BlendingMode.Additive,
|
||||||
Alpha = 0.5f,
|
Alpha = 0.5f,
|
||||||
Scale = new Vector2(2)
|
Scale = new Vector2(1.333f)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation)
|
||||||
|
{
|
||||||
|
const float large_pulp_3 = 13f;
|
||||||
|
const float distance_from_centre_3 = 0.23f;
|
||||||
|
|
||||||
|
const float large_pulp_4 = large_pulp_3 * 0.925f;
|
||||||
|
const float distance_from_centre_4 = distance_from_centre_3 / 0.925f;
|
||||||
|
|
||||||
|
const float small_pulp = large_pulp_3 / 2;
|
||||||
|
|
||||||
|
Vector2 positionAt(float angle, float distance) => new Vector2(
|
||||||
|
distance * (float)Math.Sin(angle * Math.PI / 180),
|
||||||
|
distance * (float)Math.Cos(angle * Math.PI / 180));
|
||||||
|
|
||||||
|
switch (representation)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return new Container();
|
||||||
|
case FruitVisualRepresentation.Raspberry:
|
||||||
|
return new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Framework.Graphics.Drawable[]
|
||||||
|
{
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(small_pulp),
|
||||||
|
Y = 0.05f,
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
Position = positionAt(0, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
Position = positionAt(90, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
Position = positionAt(180, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Position = positionAt(270, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case FruitVisualRepresentation.Pineapple:
|
||||||
|
return new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Framework.Graphics.Drawable[]
|
||||||
|
{
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(small_pulp),
|
||||||
|
Y = 0.1f,
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
Position = positionAt(45, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
Position = positionAt(135, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
Position = positionAt(225, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
Size = new Vector2(large_pulp_4),
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Position = positionAt(315, distance_from_centre_4),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case FruitVisualRepresentation.Pear:
|
||||||
|
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.1f,
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_3),
|
||||||
|
Position = positionAt(60, distance_from_centre_3),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_3),
|
||||||
|
Position = positionAt(180, distance_from_centre_3),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
Size = new Vector2(large_pulp_3),
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Position = positionAt(300, distance_from_centre_3),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
case FruitVisualRepresentation.Grape:
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_3),
|
||||||
|
Position = positionAt(0, distance_from_centre_3),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Size = new Vector2(large_pulp_3),
|
||||||
|
Position = positionAt(120, distance_from_centre_3),
|
||||||
|
},
|
||||||
|
new Pulp
|
||||||
|
{
|
||||||
|
Size = new Vector2(large_pulp_3),
|
||||||
|
AccentColour = AccentColour,
|
||||||
|
Position = positionAt(240, distance_from_centre_3),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,15 +16,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
public DrawableJuiceStream(JuiceStream s) : base(s)
|
public DrawableJuiceStream(JuiceStream s) : 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 (CatchHitObject tick in s.NestedHitObjects.OfType<CatchHitObject>())
|
||||||
{
|
{
|
||||||
@ -45,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddNested(DrawableHitObject<CatchHitObject> h)
|
protected override void AddNested(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||||
dropletContainer.Add(h);
|
dropletContainer.Add(h);
|
||||||
|
@ -6,18 +6,17 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
||||||
{
|
{
|
||||||
public class Pulp : Circle, IHasAccentColour
|
public class Pulp : Circle, IHasAccentColour
|
||||||
{
|
{
|
||||||
public const float PULP_SIZE = (float)CatchHitObject.OBJECT_RADIUS / 2.2f;
|
|
||||||
|
|
||||||
public Pulp()
|
public Pulp()
|
||||||
{
|
{
|
||||||
Size = new Vector2(PULP_SIZE);
|
RelativePositionAxes = Axes.Both;
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Blending = BlendingMode.Additive;
|
Blending = BlendingMode.Additive;
|
||||||
Colour = Color4.White.Opacity(0.9f);
|
Colour = Color4.White.Opacity(0.9f);
|
||||||
@ -34,8 +33,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
|||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
Radius = 5,
|
Radius = 8,
|
||||||
Colour = accentColour.Lighten(100),
|
Colour = accentColour.Darken(0.2f).Opacity(0.75f)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[Ignore("getting CI working")]
|
[Ignore("getting CI working")]
|
||||||
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseCatchPlayer() : base(typeof(CatchRuleset))
|
public TestCaseCatchPlayer() : base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseCatchStacker()
|
public TestCaseCatchStacker()
|
||||||
: base(typeof(CatchRuleset))
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
104
osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs
Normal file
104
osu.Game.Rulesets.Catch/Tests/TestCaseFruitObjects.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[Ignore("getting CI working")]
|
||||||
|
public class TestCaseFruitObjects : OsuTestCase
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(CatchHitObject),
|
||||||
|
typeof(Fruit),
|
||||||
|
typeof(Droplet),
|
||||||
|
typeof(DrawableCatchHitObject),
|
||||||
|
typeof(DrawableFruit),
|
||||||
|
typeof(DrawableDroplet),
|
||||||
|
typeof(Pulp),
|
||||||
|
};
|
||||||
|
|
||||||
|
public TestCaseFruitObjects()
|
||||||
|
{
|
||||||
|
Add(new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
createDrawable(0),
|
||||||
|
createDrawable(1),
|
||||||
|
createDrawable(2),
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
createDrawable(3),
|
||||||
|
createDrawable(4),
|
||||||
|
createDrawable(5),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private DrawableFruit createDrawable(int index)
|
||||||
|
{
|
||||||
|
var fruit = new Fruit
|
||||||
|
{
|
||||||
|
StartTime = 1000000000000,
|
||||||
|
IndexInBeatmap = index,
|
||||||
|
Scale = 1.5f,
|
||||||
|
};
|
||||||
|
|
||||||
|
fruit.ComboColour = colourForRrepesentation(fruit.VisualRepresentation);
|
||||||
|
|
||||||
|
return new DrawableFruit(fruit)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
RelativePositionAxes = Axes.Both,
|
||||||
|
Position = Vector2.Zero,
|
||||||
|
Alpha = 1,
|
||||||
|
LifetimeStart = double.NegativeInfinity,
|
||||||
|
LifetimeEnd = double.PositiveInfinity,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
||||||
|
{
|
||||||
|
switch (representation)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case FruitVisualRepresentation.Pear:
|
||||||
|
return new Color4(17, 136, 170, 255);
|
||||||
|
case FruitVisualRepresentation.Grape:
|
||||||
|
return new Color4(204, 102, 0, 255);
|
||||||
|
case FruitVisualRepresentation.Raspberry:
|
||||||
|
return new Color4(121, 9, 13, 255);
|
||||||
|
case FruitVisualRepresentation.Pineapple:
|
||||||
|
return new Color4(102, 136, 0, 255);
|
||||||
|
case FruitVisualRepresentation.Banana:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseHyperdash()
|
public TestCaseHyperdash()
|
||||||
: base(typeof(CatchRuleset))
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
{
|
{
|
||||||
public TestCasePerformancePoints()
|
public TestCasePerformancePoints()
|
||||||
: base(new CatchRuleset(new RulesetInfo()))
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,13 @@
|
|||||||
// 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 osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
using OpenTK;
|
|
||||||
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
|
||||||
{
|
{
|
||||||
@ -23,16 +22,17 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
private readonly CatcherArea catcherArea;
|
private readonly CatcherArea catcherArea;
|
||||||
|
|
||||||
public CatchPlayfield(BeatmapDifficulty difficulty)
|
public CatchPlayfield(BeatmapDifficulty difficulty)
|
||||||
: base(Axes.Y)
|
: base(ScrollingDirection.Down, BASE_WIDTH)
|
||||||
{
|
{
|
||||||
Container explodingFruitContainer;
|
Container explodingFruitContainer;
|
||||||
|
|
||||||
Reversed.Value = true;
|
|
||||||
|
|
||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.TopCentre;
|
||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
ScaledContent.Anchor = Anchor.BottomLeft;
|
||||||
|
ScaledContent.Origin = Anchor.BottomLeft;
|
||||||
|
|
||||||
|
ScaledContent.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
content = new Container<Drawable>
|
content = new Container<Drawable>
|
||||||
{
|
{
|
||||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
|
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
|
||||||
@ -63,18 +63,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
fruit.CheckPosition = CheckIfWeCanCatch;
|
fruit.CheckPosition = CheckIfWeCanCatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
|
||||||
{
|
|
||||||
if (judgement.IsHit)
|
|
||||||
{
|
|
||||||
Vector2 screenPosition = judgedObject.ScreenSpaceDrawQuad.Centre;
|
|
||||||
|
|
||||||
// todo: don't do this
|
|
||||||
(judgedObject.Parent as Container<DrawableHitObject>)?.Remove(judgedObject);
|
|
||||||
(judgedObject.Parent as Container)?.Remove(judgedObject);
|
|
||||||
|
|
||||||
catcherArea.Add(judgedObject, screenPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Scoring;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
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;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,10 @@ using osu.Framework.Input.Bindings;
|
|||||||
using osu.Framework.MathUtils;
|
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.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
@ -39,17 +42,34 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
|
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
|
||||||
{
|
{
|
||||||
fruit.RelativePositionAxes = Axes.None;
|
if (judgement.IsHit)
|
||||||
fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(absolutePosition).X - MovableCatcher.DrawSize.X / 2, 0);
|
{
|
||||||
|
var screenSpacePosition = fruit.ScreenSpaceDrawQuad.Centre;
|
||||||
|
|
||||||
fruit.Anchor = Anchor.TopCentre;
|
// todo: make this less ugly, somehow.
|
||||||
fruit.Origin = Anchor.BottomCentre;
|
(fruit.Parent as HitObjectContainer)?.Remove(fruit);
|
||||||
fruit.Scale *= 0.7f;
|
(fruit.Parent as Container)?.Remove(fruit);
|
||||||
fruit.LifetimeEnd = double.MaxValue;
|
|
||||||
|
|
||||||
MovableCatcher.Add(fruit);
|
fruit.RelativePositionAxes = Axes.None;
|
||||||
|
fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(screenSpacePosition).X - MovableCatcher.DrawSize.X / 2, 0);
|
||||||
|
|
||||||
|
fruit.Anchor = Anchor.TopCentre;
|
||||||
|
fruit.Origin = Anchor.Centre;
|
||||||
|
fruit.Scale *= 0.7f;
|
||||||
|
fruit.LifetimeEnd = double.MaxValue;
|
||||||
|
|
||||||
|
MovableCatcher.Add(fruit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fruit.HitObject.LastInCombo)
|
||||||
|
{
|
||||||
|
if (judgement.IsHit)
|
||||||
|
MovableCatcher.Explode();
|
||||||
|
else
|
||||||
|
MovableCatcher.Drop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
||||||
@ -84,12 +104,12 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
createCatcherSprite(),
|
|
||||||
caughtFruit = new Container<DrawableHitObject>
|
caughtFruit = new Container<DrawableHitObject>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
}
|
},
|
||||||
|
createCatcherSprite(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,20 +187,23 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// <param name="fruit">The fruit that was caught.</param>
|
/// <param name="fruit">The fruit that was caught.</param>
|
||||||
public void Add(DrawableHitObject fruit)
|
public void Add(DrawableHitObject fruit)
|
||||||
{
|
{
|
||||||
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
float ourRadius = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
||||||
|
float theirRadius = 0;
|
||||||
|
|
||||||
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
|
const float allowance = 6;
|
||||||
|
|
||||||
|
while (caughtFruit.Any(f =>
|
||||||
|
f.LifetimeEnd == double.MaxValue &&
|
||||||
|
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
|
||||||
{
|
{
|
||||||
fruit.X += RNG.Next(-5, 5);
|
float diff = (ourRadius + theirRadius) / allowance;
|
||||||
fruit.Y -= RNG.Next(0, 5);
|
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
|
||||||
|
fruit.Y -= RNG.NextSingle() * diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fruit.X = MathHelper.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
|
||||||
|
|
||||||
caughtFruit.Add(fruit);
|
caughtFruit.Add(fruit);
|
||||||
|
|
||||||
var catchObject = (CatchHitObject)fruit.HitObject;
|
|
||||||
|
|
||||||
if (catchObject.LastInCombo)
|
|
||||||
explode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -190,15 +213,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// <returns>Whether the catch is possible.</returns>
|
/// <returns>Whether the catch is possible.</returns>
|
||||||
public bool AttemptCatch(CatchHitObject fruit)
|
public bool AttemptCatch(CatchHitObject fruit)
|
||||||
{
|
{
|
||||||
const double relative_catcher_width = CATCHER_SIZE / 2;
|
double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f;
|
||||||
|
|
||||||
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
||||||
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
||||||
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
||||||
|
|
||||||
var validCatch =
|
var validCatch =
|
||||||
catchObjectPosition >= catcherPosition - relative_catcher_width / 2 &&
|
catchObjectPosition >= catcherPosition - halfCatcherWidth &&
|
||||||
catchObjectPosition <= catcherPosition + relative_catcher_width / 2;
|
catchObjectPosition <= catcherPosition + halfCatcherWidth;
|
||||||
|
|
||||||
if (validCatch && fruit.HyperDash)
|
if (validCatch && fruit.HyperDash)
|
||||||
{
|
{
|
||||||
@ -309,7 +332,35 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void explode()
|
/// <summary>
|
||||||
|
/// Drop any fruit off the plate.
|
||||||
|
/// </summary>
|
||||||
|
public void Drop()
|
||||||
|
{
|
||||||
|
var fruit = caughtFruit.ToArray();
|
||||||
|
|
||||||
|
foreach (var f in fruit)
|
||||||
|
{
|
||||||
|
if (ExplodingFruitTarget != null)
|
||||||
|
{
|
||||||
|
f.Anchor = Anchor.TopLeft;
|
||||||
|
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||||
|
|
||||||
|
caughtFruit.Remove(f);
|
||||||
|
|
||||||
|
ExplodingFruitTarget.Add(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
f.MoveToY(f.Y + 75, 750, Easing.InSine);
|
||||||
|
f.FadeOut(750);
|
||||||
|
f.Expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Explode any fruit off the plate.
|
||||||
|
/// </summary>
|
||||||
|
public void Explode()
|
||||||
{
|
{
|
||||||
var fruit = caughtFruit.ToArray();
|
var fruit = caughtFruit.ToArray();
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.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\TestCasePerformancePoints.cs" />
|
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||||
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
|
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
|
||||||
<Compile Include="Tests\TestCaseHyperdash.cs" />
|
<Compile Include="Tests\TestCaseHyperdash.cs" />
|
||||||
|
@ -95,7 +95,6 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new ModCinema(),
|
new ModCinema(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ManiaModGravity()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -113,7 +112,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override int LegacyID => 3;
|
public override int LegacyID => 3;
|
||||||
|
|
||||||
public ManiaRuleset(RulesetInfo rulesetInfo)
|
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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; }
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
{
|
{
|
||||||
public TestCasePerformancePoints()
|
public TestCasePerformancePoints()
|
||||||
: base(new ManiaRuleset(new RulesetInfo()))
|
: base(new ManiaRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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" />
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
@ -29,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
|
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.ComboIndex = comboIndex++;
|
obj.IndexInCurrentCombo = comboIndex++;
|
||||||
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
},
|
},
|
||||||
number = new NumberPiece
|
number = new NumberPiece
|
||||||
{
|
{
|
||||||
Text = (HitObject.ComboIndex + 1).ToString(),
|
Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
|
||||||
},
|
},
|
||||||
ring = new RingPiece(),
|
ring = new RingPiece(),
|
||||||
flash = new FlashPiece(),
|
flash = new FlashPiece(),
|
||||||
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -54,11 +54,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
StartTime = s.StartTime,
|
StartTime = s.StartTime,
|
||||||
Position = s.StackedPosition,
|
Position = s.StackedPosition,
|
||||||
ComboIndex = s.ComboIndex,
|
IndexInCurrentCombo = s.IndexInCurrentCombo,
|
||||||
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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
@ -40,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public Color4 ComboColour { get; set; } = Color4.Gray;
|
public Color4 ComboColour { get; set; } = Color4.Gray;
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
public int ComboIndex { get; set; }
|
public int IndexInCurrentCombo { get; set; }
|
||||||
|
|
||||||
public double HitWindowFor(HitResult result)
|
public double HitWindowFor(HitResult result)
|
||||||
{
|
{
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
public override int LegacyID => 0;
|
public override int LegacyID => 0;
|
||||||
|
|
||||||
public OsuRuleset(RulesetInfo rulesetInfo)
|
public OsuRuleset(RulesetInfo rulesetInfo = null)
|
||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
{
|
{
|
||||||
public TestCasePerformancePoints()
|
public TestCasePerformancePoints()
|
||||||
: base(new OsuRuleset(new RulesetInfo()))
|
: base(new OsuRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
public override int LegacyID => 1;
|
public override int LegacyID => 1;
|
||||||
|
|
||||||
public TaikoRuleset(RulesetInfo rulesetInfo)
|
public TaikoRuleset(RulesetInfo rulesetInfo = null)
|
||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
{
|
{
|
||||||
public TestCasePerformancePoints()
|
public TestCasePerformancePoints()
|
||||||
: base(new TaikoRuleset(new RulesetInfo()))
|
: base(new TaikoRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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[]
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,25 +56,25 @@ namespace osu.Game.Tests.Visual
|
|||||||
Clock = new FramedClock(),
|
Clock = new FramedClock(),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuRulesetContainer(new OsuRuleset(new RulesetInfo()), beatmap, false)
|
new OsuRulesetContainer(new OsuRuleset(), beatmap, false)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(0.5f),
|
Scale = new Vector2(0.5f),
|
||||||
Anchor = Anchor.TopLeft,
|
Anchor = Anchor.TopLeft,
|
||||||
Origin = Anchor.TopLeft
|
Origin = Anchor.TopLeft
|
||||||
},
|
},
|
||||||
new TaikoRulesetContainer(new TaikoRuleset(new RulesetInfo()),beatmap, false)
|
new TaikoRulesetContainer(new TaikoRuleset(),beatmap, false)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(0.5f),
|
Scale = new Vector2(0.5f),
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight
|
Origin = Anchor.TopRight
|
||||||
},
|
},
|
||||||
new CatchRulesetContainer(new CatchRuleset(new RulesetInfo()),beatmap, false)
|
new CatchRulesetContainer(new CatchRuleset(),beatmap, false)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(0.5f),
|
Scale = new Vector2(0.5f),
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft
|
Origin = Anchor.BottomLeft
|
||||||
},
|
},
|
||||||
new ManiaRulesetContainer(new ManiaRuleset(new RulesetInfo()),beatmap, false)
|
new ManiaRulesetContainer(new ManiaRuleset(),beatmap, false)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(0.5f),
|
Scale = new Vector2(0.5f),
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
|
184
osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
Normal file
184
osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
Normal 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -142,7 +142,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" />
|
||||||
|
@ -691,19 +691,19 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override Storyboard GetStoryboard()
|
protected override Storyboard GetStoryboard()
|
||||||
{
|
{
|
||||||
if (BeatmapInfo?.Path == null && BeatmapSetInfo?.StoryboardFile == null)
|
|
||||||
return new Storyboard();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Decoder decoder;
|
|
||||||
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo?.Path))))
|
|
||||||
decoder = Decoder.GetDecoder(stream);
|
|
||||||
|
|
||||||
// try for .osb first and fall back to .osu
|
using (var beatmap = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
||||||
string storyboardFile = BeatmapSetInfo.StoryboardFile ?? BeatmapInfo.Path;
|
{
|
||||||
using (var stream = new StreamReader(store.GetStream(getPathForFile(storyboardFile))))
|
Decoder decoder = Decoder.GetDecoder(beatmap);
|
||||||
return decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
|
|
||||||
|
if (BeatmapSetInfo?.StoryboardFile == null)
|
||||||
|
return decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap);
|
||||||
|
|
||||||
|
using (var storyboard = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
|
||||||
|
return decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap, storyboard);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public override string ShortName => "dummy";
|
public override string ShortName => "dummy";
|
||||||
|
|
||||||
public DummyRuleset(RulesetInfo rulesetInfo)
|
public DummyRuleset(RulesetInfo rulesetInfo = null)
|
||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -70,10 +70,11 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap);
|
protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap);
|
||||||
|
|
||||||
public virtual Storyboard DecodeStoryboard(StreamReader stream)
|
public virtual Storyboard DecodeStoryboard(params StreamReader[] streams)
|
||||||
{
|
{
|
||||||
var storyboard = new Storyboard();
|
var storyboard = new Storyboard();
|
||||||
ParseStoryboard(stream, storyboard);
|
foreach (StreamReader stream in streams)
|
||||||
|
ParseStoryboard(stream, storyboard);
|
||||||
return storyboard;
|
return storyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,12 +65,15 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
// Gameplay
|
// Gameplay
|
||||||
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
|
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
|
||||||
|
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
|
||||||
|
|
||||||
Set(OsuSetting.ShowInterface, true);
|
Set(OsuSetting.ShowInterface, true);
|
||||||
Set(OsuSetting.KeyOverlay, false);
|
Set(OsuSetting.KeyOverlay, false);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@ -90,6 +93,7 @@ namespace osu.Game.Configuration
|
|||||||
GameplayCursorSize,
|
GameplayCursorSize,
|
||||||
AutoCursorSize,
|
AutoCursorSize,
|
||||||
DimLevel,
|
DimLevel,
|
||||||
|
BlurLevel,
|
||||||
ShowStoryboard,
|
ShowStoryboard,
|
||||||
KeyOverlay,
|
KeyOverlay,
|
||||||
FloatingComments,
|
FloatingComments,
|
||||||
@ -114,6 +118,7 @@ namespace osu.Game.Configuration
|
|||||||
ShowFpsDisplay,
|
ShowFpsDisplay,
|
||||||
ChatDisplayHeight,
|
ChatDisplayHeight,
|
||||||
Version,
|
Version,
|
||||||
ShowConvertedBeatmaps
|
ShowConvertedBeatmaps,
|
||||||
|
SpeedChangeVisualisation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
osu.Game/Configuration/SpeedChangeVisualisationMethod.cs
Normal file
15
osu.Game/Configuration/SpeedChangeVisualisationMethod.cs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
@ -33,15 +33,6 @@ namespace osu.Game.Graphics.Containers
|
|||||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos);
|
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos);
|
||||||
|
|
||||||
protected override bool OnWheel(InputState state)
|
|
||||||
{
|
|
||||||
// always allow wheel to pass through to stuff outside our DrawRectangle.
|
|
||||||
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return BlockPassThroughMouse;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnClick(InputState state)
|
protected override bool OnClick(InputState state)
|
||||||
{
|
{
|
||||||
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
|
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,12 +3,13 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface.Volume
|
namespace osu.Game.Graphics.UserInterface.Volume
|
||||||
{
|
{
|
||||||
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>
|
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||||
{
|
{
|
||||||
public Func<GlobalAction, bool> ActionRequested;
|
public Func<GlobalAction, bool> ActionRequested;
|
||||||
|
|
||||||
|
13
osu.Game/Online/API/Requests/GetFriendsRequest.cs
Normal file
13
osu.Game/Online/API/Requests/GetFriendsRequest.cs
Normal 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";
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,12 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
Bindable = config.GetBindable<double>(OsuSetting.DimLevel),
|
Bindable = config.GetBindable<double>(OsuSetting.DimLevel),
|
||||||
KeyboardStep = 0.1f
|
KeyboardStep = 0.1f
|
||||||
},
|
},
|
||||||
|
new SettingsSlider<double>
|
||||||
|
{
|
||||||
|
LabelText = "Background blur",
|
||||||
|
Bindable = config.GetBindable<double>(OsuSetting.BlurLevel),
|
||||||
|
KeyboardStep = 0.1f
|
||||||
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show score overlay",
|
LabelText = "Show score overlay",
|
||||||
|
@ -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),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
{
|
{
|
||||||
new GeneralSettings(),
|
new GeneralSettings(),
|
||||||
new SongSelectSettings(),
|
new SongSelectSettings(),
|
||||||
|
new ScrollingSettings()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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")]
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
15
osu.Game/Overlays/Social/SocialGridPanel.cs
Normal file
15
osu.Game/Overlays/Social/SocialGridPanel.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
osu.Game/Overlays/Social/SocialListPanel.cs
Normal file
16
osu.Game/Overlays/Social/SocialListPanel.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
osu.Game/Overlays/Social/SocialPanel.cs
Normal file
60
osu.Game/Overlays/Social/SocialPanel.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,12 +36,32 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
public override bool RemoveCompletedTransforms => false;
|
public override bool RemoveCompletedTransforms => false;
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
protected override bool RequiresChildrenUpdate => true;
|
||||||
|
|
||||||
|
public virtual bool AllJudged => false;
|
||||||
|
|
||||||
protected DrawableHitObject(HitObject hitObject)
|
protected DrawableHitObject(HitObject hitObject)
|
||||||
{
|
{
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes this <see cref="DrawableHitObject"/>, checking if any judgements have occurred.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userTriggered">Whether the user triggered this process.</param>
|
||||||
|
/// <returns>Whether a judgement has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>s.</returns>
|
||||||
|
protected internal virtual bool UpdateJudgement(bool userTriggered) => false;
|
||||||
|
|
||||||
|
private List<DrawableHitObject> nestedHitObjects;
|
||||||
|
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects;
|
||||||
|
|
||||||
|
protected virtual void AddNested(DrawableHitObject h)
|
||||||
|
{
|
||||||
|
if (nestedHitObjects == null)
|
||||||
|
nestedHitObjects = new List<DrawableHitObject>();
|
||||||
|
nestedHitObjects.Add(h);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The screen-space point that causes this <see cref="DrawableHitObject"/> to be selected in the Editor.
|
/// The screen-space point that causes this <see cref="DrawableHitObject"/> to be selected in the Editor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -144,7 +164,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
|
public sealed override bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true);
|
||||||
|
|
||||||
/// <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"/>.
|
||||||
@ -180,7 +200,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="userTriggered">Whether the user triggered this process.</param>
|
/// <param name="userTriggered">Whether the user triggered this process.</param>
|
||||||
/// <returns>Whether a judgement has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>s.</returns>
|
/// <returns>Whether a judgement has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>s.</returns>
|
||||||
protected bool UpdateJudgement(bool userTriggered)
|
protected internal sealed override bool UpdateJudgement(bool userTriggered)
|
||||||
{
|
{
|
||||||
judgementOccurred = false;
|
judgementOccurred = false;
|
||||||
|
|
||||||
@ -237,18 +257,16 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
UpdateJudgement(false);
|
UpdateJudgement(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DrawableHitObject<TObject>> nestedHitObjects;
|
protected override void AddNested(DrawableHitObject h)
|
||||||
protected IEnumerable<DrawableHitObject<TObject>> NestedHitObjects => nestedHitObjects;
|
|
||||||
|
|
||||||
protected virtual void AddNested(DrawableHitObject<TObject> h)
|
|
||||||
{
|
{
|
||||||
if (nestedHitObjects == null)
|
base.AddNested(h);
|
||||||
nestedHitObjects = new List<DrawableHitObject<TObject>>();
|
|
||||||
|
|
||||||
h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
|
if (!(h is DrawableHitObject<TObject> hWithJudgement))
|
||||||
h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
|
return;
|
||||||
h.ApplyCustomUpdateState += (d, s) => ApplyCustomUpdateState?.Invoke(d, s);
|
|
||||||
nestedHitObjects.Add(h);
|
hWithJudgement.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
|
||||||
|
hWithJudgement.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
|
||||||
|
hWithJudgement.ApplyCustomUpdateState += (d, s) => ApplyCustomUpdateState?.Invoke(d, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -34,9 +34,9 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
|
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
|
||||||
|
|
||||||
protected Ruleset(RulesetInfo rulesetInfo)
|
protected Ruleset(RulesetInfo rulesetInfo = null)
|
||||||
{
|
{
|
||||||
RulesetInfo = rulesetInfo;
|
RulesetInfo = rulesetInfo ?? createRulesetInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -88,5 +88,17 @@ namespace osu.Game.Rulesets
|
|||||||
/// <param name="variant">The variant.</param>
|
/// <param name="variant">The variant.</param>
|
||||||
/// <returns>A descriptive name of the variant.</returns>
|
/// <returns>A descriptive name of the variant.</returns>
|
||||||
public virtual string GetVariantName(int variant) => string.Empty;
|
public virtual string GetVariantName(int variant) => string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a ruleset info based on this ruleset.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A filled <see cref="RulesetInfo"/>.</returns>
|
||||||
|
private RulesetInfo createRulesetInfo() => new RulesetInfo
|
||||||
|
{
|
||||||
|
Name = Description,
|
||||||
|
ShortName = ShortName,
|
||||||
|
InstantiationInfo = GetType().AssemblyQualifiedName,
|
||||||
|
ID = LegacyID
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,30 +58,21 @@ namespace osu.Game.Rulesets
|
|||||||
{
|
{
|
||||||
var context = GetContext();
|
var context = GetContext();
|
||||||
|
|
||||||
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())).ToList();
|
var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, (RulesetInfo)null)).ToList();
|
||||||
|
|
||||||
//add all legacy modes in correct order
|
//add all legacy modes in correct order
|
||||||
foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID))
|
foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID))
|
||||||
{
|
{
|
||||||
var rulesetInfo = createRulesetInfo(r);
|
if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == r.RulesetInfo.ID) == null)
|
||||||
if (context.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null)
|
context.RulesetInfo.Add(r.RulesetInfo);
|
||||||
{
|
|
||||||
context.RulesetInfo.Add(rulesetInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
|
|
||||||
//add any other modes
|
//add any other modes
|
||||||
foreach (var r in instances.Where(r => r.LegacyID < 0))
|
foreach (var r in instances.Where(r => r.LegacyID < 0))
|
||||||
{
|
if (context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == r.RulesetInfo.InstantiationInfo) == null)
|
||||||
var us = createRulesetInfo(r);
|
context.RulesetInfo.Add(r.RulesetInfo);
|
||||||
|
|
||||||
var existing = context.RulesetInfo.FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo);
|
|
||||||
|
|
||||||
if (existing == null)
|
|
||||||
context.RulesetInfo.Add(us);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.SaveChanges();
|
context.SaveChanges();
|
||||||
|
|
||||||
@ -124,13 +115,5 @@ namespace osu.Game.Rulesets
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RulesetInfo createRulesetInfo(Ruleset ruleset) => new RulesetInfo
|
|
||||||
{
|
|
||||||
Name = ruleset.Description,
|
|
||||||
ShortName = ruleset.ShortName,
|
|
||||||
InstantiationInfo = ruleset.GetType().AssemblyQualifiedName,
|
|
||||||
ID = ruleset.LegacyID
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.
|
||||||
|
@ -1,93 +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.Linq;
|
|
||||||
using osu.Framework.Caching;
|
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using OpenTK;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Timing
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A container that scrolls relative to the current time. Will autosize to the total duration of all contained hit objects along the scrolling axes.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ScrollingContainer : Container<DrawableHitObject>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the range of time that is visible by the length of the scrolling axes.
|
|
||||||
/// </summary>
|
|
||||||
public readonly BindableDouble VisibleTimeRange = new BindableDouble { Default = 1000 };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The axes through which this <see cref="ScrollingContainer"/> scrolls. This is set by the <see cref="SpeedAdjustmentContainer"/>.
|
|
||||||
/// </summary>
|
|
||||||
internal Axes ScrollingAxes;
|
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
|
||||||
protected override bool RequiresChildrenUpdate => true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The control point that defines the speed adjustments for this container. This is set by the <see cref="SpeedAdjustmentContainer"/>.
|
|
||||||
/// </summary>
|
|
||||||
internal MultiplierControlPoint ControlPoint;
|
|
||||||
|
|
||||||
private Cached<double> durationBacking;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="ScrollingContainer"/>.
|
|
||||||
/// </summary>
|
|
||||||
protected ScrollingContainer()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
RelativePositionAxes = Axes.Both;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override int Compare(Drawable x, Drawable y)
|
|
||||||
{
|
|
||||||
var hX = (DrawableHitObject)x;
|
|
||||||
var hY = (DrawableHitObject)y;
|
|
||||||
|
|
||||||
int result = hY.HitObject.StartTime.CompareTo(hX.HitObject.StartTime);
|
|
||||||
if (result != 0)
|
|
||||||
return result;
|
|
||||||
return base.Compare(y, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Add(DrawableHitObject drawable)
|
|
||||||
{
|
|
||||||
durationBacking.Invalidate();
|
|
||||||
base.Add(drawable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Remove(DrawableHitObject drawable)
|
|
||||||
{
|
|
||||||
durationBacking.Invalidate();
|
|
||||||
return base.Remove(drawable);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Todo: This may underestimate the size of the hit object in some cases, but won't be too much of a problem for now
|
|
||||||
private double computeDuration() => Math.Max(0, Children.Select(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime).DefaultIfEmpty().Max() - ControlPoint.StartTime) + 1000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An approximate total duration of this scrolling container.
|
|
||||||
/// </summary>
|
|
||||||
public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration());
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0);
|
|
||||||
|
|
||||||
// We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects
|
|
||||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y);
|
|
||||||
// And we need to make sure the hit object's position-space doesn't change due to our resizing
|
|
||||||
RelativeChildSize = Size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,114 +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.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Timing
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A container that provides the speed adjustments defined by <see cref="MultiplierControlPoint"/>s to affect the scroll speed
|
|
||||||
/// of container <see cref="DrawableHitObject"/>s.
|
|
||||||
/// </summary>
|
|
||||||
public class SpeedAdjustmentContainer : Container<DrawableHitObject>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the range of time that is visible by the length of the scrolling axes.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Bindable<double> VisibleTimeRange = new Bindable<double> { Default = 1000 };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether to reverse the scrolling direction is reversed.
|
|
||||||
/// </summary>
|
|
||||||
public readonly BindableBool Reversed = new BindableBool();
|
|
||||||
|
|
||||||
protected override Container<DrawableHitObject> Content => content;
|
|
||||||
private readonly Container<DrawableHitObject> content;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The axes which the content of this container will scroll through.
|
|
||||||
/// </summary>
|
|
||||||
public Axes ScrollingAxes
|
|
||||||
{
|
|
||||||
get { return scrollingContainer.ScrollingAxes; }
|
|
||||||
set { scrollingContainer.ScrollingAxes = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
|
||||||
protected override bool RequiresChildrenUpdate => true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.
|
|
||||||
/// </summary>
|
|
||||||
public readonly MultiplierControlPoint ControlPoint;
|
|
||||||
|
|
||||||
private readonly ScrollingContainer scrollingContainer;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="SpeedAdjustmentContainer"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controlPoint">The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.</param>
|
|
||||||
public SpeedAdjustmentContainer(MultiplierControlPoint controlPoint)
|
|
||||||
{
|
|
||||||
ControlPoint = controlPoint;
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
scrollingContainer = CreateScrollingContainer();
|
|
||||||
scrollingContainer.ControlPoint = ControlPoint;
|
|
||||||
scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange);
|
|
||||||
|
|
||||||
AddInternal(content = scrollingContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
float multiplier = (float)ControlPoint.Multiplier;
|
|
||||||
|
|
||||||
// The speed adjustment happens by modifying our size by the multiplier while maintaining the visible time range as the relatve size for our children
|
|
||||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? multiplier : 1, (ScrollingAxes & Axes.Y) > 0 ? multiplier : 1);
|
|
||||||
|
|
||||||
if (Reversed)
|
|
||||||
{
|
|
||||||
RelativeChildSize = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)-VisibleTimeRange : 1, (ScrollingAxes & Axes.Y) > 0 ? (float)-VisibleTimeRange : 1);
|
|
||||||
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)VisibleTimeRange : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)VisibleTimeRange : 0);
|
|
||||||
Origin = Anchor = Anchor.BottomRight;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RelativeChildSize = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)VisibleTimeRange : 1, (ScrollingAxes & Axes.Y) > 0 ? (float)VisibleTimeRange : 1);
|
|
||||||
RelativeChildOffset = Vector2.Zero;
|
|
||||||
Origin = Anchor = Anchor.TopLeft;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override double LifetimeStart => ControlPoint.StartTime - VisibleTimeRange;
|
|
||||||
public override double LifetimeEnd => ControlPoint.StartTime + scrollingContainer.Duration + VisibleTimeRange;
|
|
||||||
|
|
||||||
public override void Add(DrawableHitObject drawable)
|
|
||||||
{
|
|
||||||
var scrollingHitObject = drawable as IScrollingHitObject;
|
|
||||||
|
|
||||||
if (scrollingHitObject != null)
|
|
||||||
{
|
|
||||||
scrollingHitObject.LifetimeOffset.BindTo(VisibleTimeRange);
|
|
||||||
scrollingHitObject.ScrollingAxes = ScrollingAxes;
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Add(drawable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether a point in time falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
|
||||||
/// </summary>
|
|
||||||
public bool CanContain(double startTime) => ControlPoint.StartTime <= startTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the <see cref="ScrollingContainer"/> which contains the scrolling <see cref="DrawableHitObject"/>s of this container.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The <see cref="ScrollingContainer"/>.</returns>
|
|
||||||
protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ControlPoint);
|
|
||||||
}
|
|
||||||
}
|
|
19
osu.Game/Rulesets/UI/HitObjectContainer.cs
Normal file
19
osu.Game/Rulesets/UI/HitObjectContainer.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.UI
|
||||||
|
{
|
||||||
|
public class HitObjectContainer : CompositeDrawable
|
||||||
|
{
|
||||||
|
public virtual IEnumerable<DrawableHitObject> Objects => InternalChildren.Cast<DrawableHitObject>();
|
||||||
|
public virtual IEnumerable<DrawableHitObject> AliveObjects => AliveInternalChildren.Cast<DrawableHitObject>();
|
||||||
|
|
||||||
|
public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject);
|
||||||
|
public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject);
|
||||||
|
}
|
||||||
|
}
|
@ -8,8 +8,6 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
namespace osu.Game.Rulesets.UI
|
||||||
{
|
{
|
||||||
@ -18,7 +16,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The HitObjects contained in this Playfield.
|
/// The HitObjects contained in this Playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HitObjectContainer HitObjects { get; protected set; }
|
public HitObjectContainer HitObjects { get; private set; }
|
||||||
|
|
||||||
public Container<Drawable> ScaledContent;
|
public Container<Drawable> ScaledContent;
|
||||||
|
|
||||||
@ -51,16 +49,14 @@ namespace osu.Game.Rulesets.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
HitObjects = new HitObjectContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
HitObjects = CreateHitObjectContainer();
|
||||||
|
HitObjects.RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
Add(HitObjects);
|
Add(HitObjects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +90,10 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <param name="judgement">The <see cref="Judgement"/> that occurred.</param>
|
/// <param name="judgement">The <see cref="Judgement"/> that occurred.</param>
|
||||||
public virtual void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { }
|
public virtual void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { }
|
||||||
|
|
||||||
public class HitObjectContainer : CompositeDrawable
|
/// <summary>
|
||||||
{
|
/// Creates the container that will be used to contain the <see cref="DrawableHitObject"/>s.
|
||||||
public virtual IEnumerable<DrawableHitObject> Objects => InternalChildren.OfType<DrawableHitObject>();
|
/// </summary>
|
||||||
public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject);
|
protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
|
||||||
public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ScaledContainer : Container
|
private class ScaledContainer : Container
|
||||||
{
|
{
|
||||||
@ -110,6 +104,12 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
//dividing by the customwidth will effectively scale our content to the required container size.
|
//dividing by the customwidth will effectively scale our content to the required container size.
|
||||||
protected override Vector2 DrawScale => CustomWidth.HasValue ? new Vector2(DrawSize.X / CustomWidth.Value) : base.DrawScale;
|
protected override Vector2 DrawScale => CustomWidth.HasValue ? new Vector2(DrawSize.X / CustomWidth.Value) : base.DrawScale;
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
RelativeChildSize = new Vector2(DrawScale.X, RelativeChildSize.Y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs
Normal file
25
osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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.UI.Scrolling
|
||||||
|
{
|
||||||
|
public enum ScrollingDirection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hitobjects will scroll vertically from the bottom of the hitobject container.
|
||||||
|
/// </summary>
|
||||||
|
Up,
|
||||||
|
/// <summary>
|
||||||
|
/// Hitobjects will scroll vertically from the top of the hitobject container.
|
||||||
|
/// </summary>
|
||||||
|
Down,
|
||||||
|
/// <summary>
|
||||||
|
/// Hitobjects will scroll horizontally from the right of the hitobject container.
|
||||||
|
/// </summary>
|
||||||
|
Left,
|
||||||
|
/// <summary>
|
||||||
|
/// Hitobjects will scroll horizontally from the left of the hitobject container.
|
||||||
|
/// </summary>
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
}
|
116
osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
Normal file
116
osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// 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.Framework.Caching;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Lists;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Timing;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling.Visualisers;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.UI.Scrolling
|
||||||
|
{
|
||||||
|
public class ScrollingHitObjectContainer : HitObjectContainer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The duration required to scroll through one length of the <see cref="ScrollingHitObjectContainer"/> before any control point adjustments.
|
||||||
|
/// </summary>
|
||||||
|
public readonly BindableDouble TimeRange = new BindableDouble
|
||||||
|
{
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = double.MaxValue
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The control points that adjust the scrolling speed.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
||||||
|
|
||||||
|
private readonly ScrollingDirection direction;
|
||||||
|
|
||||||
|
private Cached initialStateCache = new Cached();
|
||||||
|
|
||||||
|
public ScrollingHitObjectContainer(ScrollingDirection direction)
|
||||||
|
{
|
||||||
|
this.direction = direction;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
TimeRange.ValueChanged += v => initialStateCache.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISpeedChangeVisualiser speedChangeVisualiser;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config)
|
||||||
|
{
|
||||||
|
switch (config.Get<SpeedChangeVisualisationMethod>(OsuSetting.SpeedChangeVisualisation))
|
||||||
|
{
|
||||||
|
case SpeedChangeVisualisationMethod.Sequential:
|
||||||
|
speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints);
|
||||||
|
break;
|
||||||
|
case SpeedChangeVisualisationMethod.Overlapping:
|
||||||
|
speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Add(DrawableHitObject hitObject)
|
||||||
|
{
|
||||||
|
initialStateCache.Invalidate();
|
||||||
|
base.Add(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Remove(DrawableHitObject hitObject)
|
||||||
|
{
|
||||||
|
var result = base.Remove(hitObject);
|
||||||
|
if (result)
|
||||||
|
initialStateCache.Invalidate();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddControlPoint(MultiplierControlPoint controlPoint)
|
||||||
|
{
|
||||||
|
ControlPoints.Add(controlPoint);
|
||||||
|
initialStateCache.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveControlPoint(MultiplierControlPoint controlPoint)
|
||||||
|
{
|
||||||
|
var result = ControlPoints.Remove(controlPoint);
|
||||||
|
if (result)
|
||||||
|
initialStateCache.Invalidate();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
|
||||||
|
{
|
||||||
|
if ((invalidation & (Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo)) > 0)
|
||||||
|
initialStateCache.Invalidate();
|
||||||
|
|
||||||
|
return base.Invalidate(invalidation, source, shallPropagate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!initialStateCache.IsValid)
|
||||||
|
{
|
||||||
|
speedChangeVisualiser.ComputeInitialStates(Objects, direction, TimeRange, DrawSize);
|
||||||
|
initialStateCache.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildrenLife()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildrenLife();
|
||||||
|
|
||||||
|
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
||||||
|
speedChangeVisualiser.ComputePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
Normal file
142
osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Transforms;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using OpenTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.UI.Scrolling
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A type of <see cref="Playfield"/> specialized towards scrolling <see cref="DrawableHitObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ScrollingPlayfield : Playfield
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default span of time visible by the length of the scrolling axes.
|
||||||
|
/// This is clamped between <see cref="time_span_min"/> and <see cref="time_span_max"/>.
|
||||||
|
/// </summary>
|
||||||
|
private const double time_span_default = 1500;
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum span of time that may be visible by the length of the scrolling axes.
|
||||||
|
/// </summary>
|
||||||
|
private const double time_span_min = 50;
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum span of time that may be visible by the length of the scrolling axes.
|
||||||
|
/// </summary>
|
||||||
|
private const double time_span_max = 10000;
|
||||||
|
/// <summary>
|
||||||
|
/// The step increase/decrease of the span of time visible by the length of the scrolling axes.
|
||||||
|
/// </summary>
|
||||||
|
private const double time_span_step = 50;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The span of time that is visible by the length of the scrolling axes.
|
||||||
|
/// For example, only hit objects with start time less than or equal to 1000 will be visible with <see cref="VisibleTimeRange"/> = 1000.
|
||||||
|
/// </summary>
|
||||||
|
public readonly BindableDouble VisibleTimeRange = new BindableDouble(time_span_default)
|
||||||
|
{
|
||||||
|
Default = time_span_default,
|
||||||
|
MinValue = time_span_min,
|
||||||
|
MaxValue = time_span_max
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the player can change <see cref="VisibleTimeRange"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool UserScrollSpeedAdjustment => true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The container that contains the <see cref="SpeedAdjustmentContainer"/>s and <see cref="DrawableHitObject"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
|
||||||
|
|
||||||
|
private readonly ScrollingDirection direction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ScrollingPlayfield"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scrollingAxes">The axes on which <see cref="DrawableHitObject"/>s in this container should scroll.</param>
|
||||||
|
/// <param name="customWidth">Whether we want our internal coordinate system to be scaled to a specified width</param>
|
||||||
|
protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null)
|
||||||
|
: base(customWidth)
|
||||||
|
{
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
HitObjects.TimeRange.BindTo(VisibleTimeRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ScrollingPlayfield> nestedPlayfields;
|
||||||
|
/// <summary>
|
||||||
|
/// All the <see cref="ScrollingPlayfield"/>s nested inside this playfield.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<ScrollingPlayfield> NestedPlayfields => nestedPlayfields;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a <see cref="ScrollingPlayfield"/> to this playfield. The nested <see cref="ScrollingPlayfield"/>
|
||||||
|
/// will be given all of the same speed adjustments as this playfield.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="otherPlayfield">The <see cref="ScrollingPlayfield"/> to add.</param>
|
||||||
|
protected void AddNested(ScrollingPlayfield otherPlayfield)
|
||||||
|
{
|
||||||
|
if (nestedPlayfields == null)
|
||||||
|
nestedPlayfields = new List<ScrollingPlayfield>();
|
||||||
|
|
||||||
|
nestedPlayfields.Add(otherPlayfield);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
|
{
|
||||||
|
if (!UserScrollSpeedAdjustment)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (state.Keyboard.ControlPressed)
|
||||||
|
{
|
||||||
|
switch (args.Key)
|
||||||
|
{
|
||||||
|
case Key.Minus:
|
||||||
|
transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint);
|
||||||
|
break;
|
||||||
|
case Key.Plus:
|
||||||
|
transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None)
|
||||||
|
{
|
||||||
|
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction);
|
||||||
|
|
||||||
|
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
|
||||||
|
{
|
||||||
|
private double valueAt(double time)
|
||||||
|
{
|
||||||
|
if (time < StartTime) return StartValue;
|
||||||
|
if (time >= EndTime) return EndValue;
|
||||||
|
|
||||||
|
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string TargetMember => "VisibleTimeRange.Value";
|
||||||
|
|
||||||
|
protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time);
|
||||||
|
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Timing;
|
using osu.Game.Rulesets.Timing;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
namespace osu.Game.Rulesets.UI.Scrolling
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A type of <see cref="RulesetContainer{TPlayfield,TObject}"/> that supports a <see cref="ScrollingPlayfield"/>.
|
/// A type of <see cref="RulesetContainer{TPlayfield,TObject}"/> that supports a <see cref="ScrollingPlayfield"/>.
|
||||||
@ -70,12 +70,10 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
// Perform some post processing of the timing changes
|
// Perform some post processing of the timing changes
|
||||||
timingChanges = timingChanges
|
timingChanges = timingChanges
|
||||||
// Collapse sections after the last hit object
|
// Collapse sections after the last hit object
|
||||||
.Where(s => s.StartTime <= lastObjectTime)
|
.Where(s => s.StartTime <= lastObjectTime)
|
||||||
// Collapse sections with the same start time
|
// Collapse sections with the same start time
|
||||||
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime)
|
.GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime);
|
||||||
// Collapse sections with the same beat length
|
|
||||||
.GroupBy(s => s.TimingPoint.BeatLength * s.DifficultyPoint.SpeedMultiplier).Select(g => g.First());
|
|
||||||
|
|
||||||
DefaultControlPoints.AddRange(timingChanges);
|
DefaultControlPoints.AddRange(timingChanges);
|
||||||
|
|
||||||
@ -88,7 +86,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
|
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield playfield)
|
||||||
{
|
{
|
||||||
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
|
playfield.HitObjects.AddControlPoint(controlPoint);
|
||||||
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
|
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,12 +106,5 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone());
|
return new MultiplierControlPoint(time, DefaultControlPoints[index].DeepClone());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a <see cref="SpeedAdjustmentContainer"/> that facilitates the movement of hit objects.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controlPoint">The <see cref="MultiplierControlPoint"/> that provides the speed adjustments for the hitobjects.</param>
|
|
||||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/>.</returns>
|
|
||||||
protected virtual SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new SpeedAdjustmentContainer(controlPoint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
// 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.Objects.Drawables;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||||
|
{
|
||||||
|
public interface ISpeedChangeVisualiser
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the states of <see cref="DrawableHitObject"/>s that are constant, such as lifetime and spatial length.
|
||||||
|
/// This is invoked once whenever <paramref name="timeRange"/> or <paramref name="length"/> changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||||
|
/// <param name="direction">The scrolling direction.</param>
|
||||||
|
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||||
|
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||||
|
void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the states of <see cref="DrawableHitObject"/>s that change depending on <paramref name="currentTime"/>, such as position.
|
||||||
|
/// This is invoked once per frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObjects">The <see cref="DrawableHitObject"/>s whose states should be computed.</param>
|
||||||
|
/// <param name="direction">The scrolling direction.</param>
|
||||||
|
/// <param name="currentTime">The current time.</param>
|
||||||
|
/// <param name="timeRange">The duration required to scroll through one length of the screen before any control point adjustments.</param>
|
||||||
|
/// <param name="length">The length of the screen that is scrolled through.</param>
|
||||||
|
void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
// 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.Lists;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Timing;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
||||||
|
{
|
||||||
|
public class OverlappingSpeedChangeVisualiser : ISpeedChangeVisualiser
|
||||||
|
{
|
||||||
|
private readonly SortedList<MultiplierControlPoint> controlPoints;
|
||||||
|
|
||||||
|
public OverlappingSpeedChangeVisualiser(SortedList<MultiplierControlPoint> controlPoints)
|
||||||
|
{
|
||||||
|
this.controlPoints = controlPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ComputeInitialStates(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double timeRange, Vector2 length)
|
||||||
|
{
|
||||||
|
foreach (var obj in hitObjects)
|
||||||
|
{
|
||||||
|
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||||
|
obj.LifetimeStart = obj.HitObject.StartTime - timeRange / controlPoint.Multiplier;
|
||||||
|
|
||||||
|
if (obj.NestedHitObjects != null)
|
||||||
|
{
|
||||||
|
ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length);
|
||||||
|
ComputePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ComputePositions(IEnumerable<DrawableHitObject> hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length)
|
||||||
|
{
|
||||||
|
foreach (var obj in hitObjects)
|
||||||
|
{
|
||||||
|
var controlPoint = controlPointAt(obj.HitObject.StartTime);
|
||||||
|
|
||||||
|
var position = (obj.HitObject.StartTime - currentTime) * controlPoint.Multiplier / timeRange;
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case ScrollingDirection.Up:
|
||||||
|
obj.Y = (float)(position * length.Y);
|
||||||
|
break;
|
||||||
|
case ScrollingDirection.Down:
|
||||||
|
obj.Y = (float)(-position * length.Y);
|
||||||
|
break;
|
||||||
|
case ScrollingDirection.Left:
|
||||||
|
obj.X = (float)(position * length.X);
|
||||||
|
break;
|
||||||
|
case ScrollingDirection.Right:
|
||||||
|
obj.X = (float)(-position * length.X);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly MultiplierControlPoint searchPoint = new MultiplierControlPoint();
|
||||||
|
private MultiplierControlPoint controlPointAt(double time)
|
||||||
|
{
|
||||||
|
if (controlPoints.Count == 0)
|
||||||
|
return new MultiplierControlPoint(double.NegativeInfinity);
|
||||||
|
|
||||||
|
if (time < controlPoints[0].StartTime)
|
||||||
|
return controlPoints[0];
|
||||||
|
|
||||||
|
searchPoint.StartTime = time;
|
||||||
|
int index = controlPoints.BinarySearch(searchPoint);
|
||||||
|
|
||||||
|
if (index < 0)
|
||||||
|
index = ~index - 1;
|
||||||
|
|
||||||
|
return controlPoints[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user