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

Merge remote-tracking branch 'upstream/master' into sliderbar-tooltip-precision

This commit is contained in:
Dean Herbert 2018-01-12 18:54:36 +09:00
commit 188b26472c
185 changed files with 3074 additions and 2211 deletions

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
if (beatmap.ComboColors.Count == 0)
return;
int comboIndex = 0;
int index = 0;
int colourIndex = 0;
CatchHitObject lastObj = null;
@ -31,12 +31,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
if (obj.NewCombo)
{
if (lastObj != null) lastObj.LastInCombo = true;
comboIndex = 0;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
}
obj.ComboIndex = comboIndex++;
obj.IndexInBeatmap = index++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
lastObj = obj;

View File

@ -10,6 +10,7 @@ using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch
{
@ -80,7 +81,7 @@ namespace osu.Game.Rulesets.Catch
{
Mods = new Mod[]
{
new ModAutoplay(),
new ModAutoplay<CatchHitObject>(),
new ModCinema(),
},
},
@ -101,7 +102,7 @@ namespace osu.Game.Rulesets.Catch
public override int LegacyID => 2;
public CatchRuleset(RulesetInfo rulesetInfo)
public CatchRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}

View File

@ -1,69 +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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNoFail : ModNoFail
{
}
public class CatchModEasy : ModEasy
{
}
public class CatchModHidden : ModHidden
{
public override string Description => @"Play with fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06;
}
public class CatchModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
}
public class CatchModSuddenDeath : ModSuddenDeath
{
}
public class CatchModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
}
public class CatchModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.06;
}
public class CatchModRelax : ModRelax
{
public override string Description => @"Use the mouse to control the catcher.";
}
public class CatchModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
}
public class CatchModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.06;
}
public class CatchModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.12;
}
public class CatchModPerfect : ModPerfect
{
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.06;
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModEasy : ModEasy
{
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.12;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHidden : ModHidden
{
public override string Description => @"Play with fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.06;
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNoFail : ModNoFail
{
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModPerfect : ModPerfect
{
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModRelax : ModRelax
{
public override string Description => @"Use the mouse to control the catcher.";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModSuddenDeath : ModSuddenDeath
{
}
}

View File

@ -15,8 +15,11 @@ namespace osu.Game.Rulesets.Catch.Objects
public float X { get; set; }
public Color4 ComboColour { get; set; } = Color4.Gray;
public int ComboIndex { get; set; }
public Color4 ComboColour { get; set; }
public int IndexInBeatmap { get; set; }
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
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;
}
}
public enum FruitVisualRepresentation
{
Pear,
Grape,
Raspberry,
Pineapple,
Banana // banananananannaanana
}
}

View File

@ -10,6 +10,17 @@ using osu.Game.Rulesets.Scoring;
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
where TObject : CatchHitObject
{
@ -19,19 +30,17 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: base(hitObject)
{
HitObject = hitObject;
Scale = new Vector2(HitObject.Scale);
Anchor = Anchor.BottomLeft;
}
}
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchHitObject>
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
{
protected DrawableCatchHitObject(CatchHitObject hitObject)
: base(hitObject)
{
RelativePositionAxes = Axes.Both;
RelativePositionAxes = Axes.X;
X = hitObject.X;
Y = (float)HitObject.StartTime;
}
public Func<CatchHitObject, bool> CheckPosition;

View File

@ -8,15 +8,13 @@ using OpenTK;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableDroplet : DrawableCatchHitObject<Droplet>
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
{
public DrawableDroplet(Droplet h)
: base(h)
{
Origin = Anchor.Centre;
Size = new Vector2(Pulp.PULP_SIZE);
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
AccentColour = h.ComboColour;
Masking = false;
}
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Child = new Pulp
{
AccentColour = AccentColour,
Scale = new Vector2(0.8f),
Size = Size
};
}
}

View File

@ -1,9 +1,12 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
@ -11,14 +14,16 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableFruit : DrawableCatchHitObject<Fruit>
public class DrawableFruit : PalpableCatchHitObject<Fruit>
{
private Circle border;
public DrawableFruit(Fruit h)
: base(h)
{
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;
Masking = false;
@ -28,48 +33,34 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
[BackgroundDependencyLoader]
private void load()
{
Children = new[]
{
createPulp(HitObject.VisualRepresentation),
border = new Circle
{
EdgeEffect = new EdgeEffectParameters
{
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[]
{
//todo: share this more
new BufferedContainer
new Box
{
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = AccentColour,
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,
},
AlwaysPresent = true,
Colour = AccentColour,
Alpha = 0,
RelativeSizeAxes = Axes.Both
}
}
},
};
if (HitObject.HyperDash)
@ -82,9 +73,184 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AccentColour = Color4.Red,
Blending = BlendingMode.Additive,
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);
}
}
}

View File

@ -16,15 +16,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public DrawableJuiceStream(JuiceStream s) : base(s)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
Origin = Anchor.BottomLeft;
X = 0;
Child = dropletContainer = new Container
{
RelativeSizeAxes = Axes.Both,
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
};
Child = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
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;
dropletContainer.Add(h);

View File

@ -6,18 +6,17 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
{
public class Pulp : Circle, IHasAccentColour
{
public const float PULP_SIZE = (float)CatchHitObject.OBJECT_RADIUS / 2.2f;
public Pulp()
{
Size = new Vector2(PULP_SIZE);
RelativePositionAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Blending = BlendingMode.Additive;
Colour = Color4.White.Opacity(0.9f);
@ -34,8 +33,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Lighten(100),
Radius = 8,
Colour = accentColour.Darken(0.2f).Opacity(0.75f)
};
}
}

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Tests
[Ignore("getting CI working")]
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
{
public TestCaseCatchPlayer() : base(typeof(CatchRuleset))
public TestCaseCatchPlayer() : base(new CatchRuleset())
{
}
}

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
{
public TestCaseCatchStacker()
: base(typeof(CatchRuleset))
: base(new CatchRuleset())
{
}

View 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);
}
}
}
}
}

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
{
public TestCaseHyperdash()
: base(typeof(CatchRuleset))
: base(new CatchRuleset())
{
}

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new CatchRuleset(new RulesetInfo()))
: base(new CatchRuleset())
{
}
}

View File

@ -2,14 +2,13 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.UI;
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
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.UI.Scrolling;
namespace osu.Game.Rulesets.Catch.UI
{
@ -23,16 +22,17 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly CatcherArea catcherArea;
public CatchPlayfield(BeatmapDifficulty difficulty)
: base(Axes.Y)
: base(ScrollingDirection.Down, BASE_WIDTH)
{
Container explodingFruitContainer;
Reversed.Value = true;
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
InternalChildren = new Drawable[]
ScaledContent.Anchor = Anchor.BottomLeft;
ScaledContent.Origin = Anchor.BottomLeft;
ScaledContent.AddRange(new Drawable[]
{
content = new Container<Drawable>
{
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
}
};
});
}
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
@ -63,18 +63,6 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.CheckPosition = CheckIfWeCanCatch;
}
public override void OnJudgement(DrawableHitObject judgedObject, Judgement 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);
}
}
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
}
}

View File

@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Catch.UI
{

View File

@ -12,7 +12,10 @@ using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
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.UI;
using OpenTK;
using OpenTK.Graphics;
@ -39,19 +42,36 @@ namespace osu.Game.Rulesets.Catch.UI
};
}
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
{
if (judgement.IsHit)
{
var screenSpacePosition = fruit.ScreenSpaceDrawQuad.Centre;
// todo: make this less ugly, somehow.
(fruit.Parent as HitObjectContainer)?.Remove(fruit);
(fruit.Parent as Container)?.Remove(fruit);
fruit.RelativePositionAxes = Axes.None;
fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(absolutePosition).X - MovableCatcher.DrawSize.X / 2, 0);
fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(screenSpacePosition).X - MovableCatcher.DrawSize.X / 2, 0);
fruit.Anchor = Anchor.TopCentre;
fruit.Origin = Anchor.BottomCentre;
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 class Catcher : Container, IKeyBindingHandler<CatchAction>
@ -84,12 +104,12 @@ namespace osu.Game.Rulesets.Catch.UI
Children = new Drawable[]
{
createCatcherSprite(),
caughtFruit = new Container<DrawableHitObject>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
}
},
createCatcherSprite(),
};
}
@ -167,20 +187,23 @@ namespace osu.Game.Rulesets.Catch.UI
/// <param name="fruit">The fruit that was caught.</param>
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);
fruit.Y -= RNG.Next(0, 5);
float diff = (ourRadius + theirRadius) / allowance;
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);
var catchObject = (CatchHitObject)fruit.HitObject;
if (catchObject.LastInCombo)
explode();
}
/// <summary>
@ -190,15 +213,15 @@ namespace osu.Game.Rulesets.Catch.UI
/// <returns>Whether the catch is possible.</returns>
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.
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
var validCatch =
catchObjectPosition >= catcherPosition - relative_catcher_width / 2 &&
catchObjectPosition <= catcherPosition + relative_catcher_width / 2;
catchObjectPosition >= catcherPosition - halfCatcherWidth &&
catchObjectPosition <= catcherPosition + halfCatcherWidth;
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);
}
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();

View File

@ -49,6 +49,17 @@
<Compile Include="Beatmaps\CatchBeatmapProcessor.cs" />
<Compile Include="CatchDifficultyCalculator.cs" />
<Compile Include="CatchInputManager.cs" />
<Compile Include="Mods\CatchModDaycore.cs" />
<Compile Include="Mods\CatchModDoubleTime.cs" />
<Compile Include="Mods\CatchModEasy.cs" />
<Compile Include="Mods\CatchModFlashlight.cs" />
<Compile Include="Mods\CatchModHalfTime.cs" />
<Compile Include="Mods\CatchModHardRock.cs" />
<Compile Include="Mods\CatchModHidden.cs" />
<Compile Include="Mods\CatchModNightcore.cs" />
<Compile Include="Mods\CatchModPerfect.cs" />
<Compile Include="Mods\CatchModRelax.cs" />
<Compile Include="Mods\CatchModSuddenDeath.cs" />
<Compile Include="Objects\Drawable\DrawableCatchHitObject.cs" />
<Compile Include="Objects\Drawable\DrawableDroplet.cs" />
<Compile Include="Objects\Drawable\DrawableJuiceStream.cs" />
@ -64,6 +75,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tests\TestCaseCatcherArea.cs" />
<Compile Include="Tests\TestCaseCatchStacker.cs" />
<Compile Include="Tests\TestCaseFruitObjects.cs" />
<Compile Include="Tests\TestCasePerformancePoints.cs" />
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
<Compile Include="Tests\TestCaseHyperdash.cs" />
@ -71,7 +83,7 @@
<Compile Include="UI\CatchRulesetContainer.cs" />
<Compile Include="UI\CatchPlayfield.cs" />
<Compile Include="CatchRuleset.cs" />
<Compile Include="Mods\CatchMod.cs" />
<Compile Include="Mods\CatchModNoFail.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />

View File

@ -95,7 +95,6 @@ namespace osu.Game.Rulesets.Mania
new ModCinema(),
},
},
new ManiaModGravity()
};
default:
@ -113,7 +112,7 @@ namespace osu.Game.Rulesets.Mania
public override int LegacyID => 3;
public ManiaRuleset(RulesetInfo rulesetInfo)
public ManiaRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}

View File

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

View File

@ -0,0 +1,29 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter<ManiaHitObject>
{
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
{
var mbc = (ManiaBeatmapConverter)beatmapConverter;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (mbc.IsForCurrentRuleset)
return;
mbc.TargetColumns = KeyCount;
}
}
}

View File

@ -1,197 +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.Graphics;
using osu.Game.Rulesets.Mods;
using System;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModNoFail : ModNoFail
{
}
public class ManiaModEasy : ModEasy
{
}
public class ManiaModHidden : ModHidden
{
public override string Description => @"The notes fade out before you hit them!";
public override double ScoreMultiplier => 1.0;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
public class ManiaModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.0;
}
public class ManiaModSuddenDeath : ModSuddenDeath
{
}
public class ManiaModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.3;
}
public class ManiaModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.0;
}
public class ManiaModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.3;
}
public class ManiaModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.0;
}
public class ManiaModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.0;
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
}
public class ManiaModPerfect : ModPerfect
{
}
public class ManiaModFadeIn : Mod
{
public override string Name => "FadeIn";
public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
public class ManiaModRandom : Mod, IApplicableToRulesetContainer<ManiaHitObject>
{
public override string Name => "Random";
public override string ShortenedName => "RD";
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
int availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
}
}
public abstract class ManiaKeyMod : Mod, IApplicableMod, IApplicableToBeatmapConverter<ManiaHitObject>
{
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
{
var mbc = (ManiaBeatmapConverter)beatmapConverter;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (mbc.IsForCurrentRuleset)
return;
mbc.TargetColumns = KeyCount;
}
}
public class ManiaModKey1 : ManiaKeyMod
{
public override int KeyCount => 1;
public override string Name => "1K";
}
public class ManiaModKey2 : ManiaKeyMod
{
public override int KeyCount => 2;
public override string Name => "2K";
}
public class ManiaModKey3 : ManiaKeyMod
{
public override int KeyCount => 3;
public override string Name => "3K";
}
public class ManiaModKey4 : ManiaKeyMod
{
public override int KeyCount => 4;
public override string Name => "4K";
}
public class ManiaModKey5 : ManiaKeyMod
{
public override int KeyCount => 5;
public override string Name => "5K";
}
public class ManiaModKey6 : ManiaKeyMod
{
public override int KeyCount => 6;
public override string Name => "6K";
}
public class ManiaModKey7 : ManiaKeyMod
{
public override int KeyCount => 7;
public override string Name => "7K";
}
public class ManiaModKey8 : ManiaKeyMod
{
public override int KeyCount => 8;
public override string Name => "8K";
}
public class ManiaModKey9 : ManiaKeyMod
{
public override int KeyCount => 9;
public override string Name => "9K";
}
public class ManiaModKeyCoop : Mod
{
public override string Name => "KeyCoop";
public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
}
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
{
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
{
User = new User { Username = "osu!topus!" },
Replay = new ManiaAutoGenerator(beatmap).Generate(),
};
}
}

View File

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

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.3;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.0;
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModEasy : ModEasy
{
}
}

View File

@ -0,0 +1,20 @@
// 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 osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModFadeIn : Mod
{
public override string Name => "FadeIn";
public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
}

View File

@ -0,0 +1,14 @@
// 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 osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.0;
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
}
}

View File

@ -1,48 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.Mania.Objects.Drawables;
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 (ManiaHitObject obj in rulesetContainer.Objects.OfType<ManiaHitObject>())
{
MultiplierControlPoint 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 (DrawableBarLine barLine in rulesetContainer.BarLines)
{
var controlPoint = rulesetContainer.CreateControlPointAt(barLine.HitObject.StartTime);
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now
controlPoint.TimingPoint.BeatLength = 1000;
barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity));
}
}
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.3;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.0;
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModHidden : ModHidden
{
public override string Description => @"The notes fade out before you hit them!";
public override double ScoreMultiplier => 1.0;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey1 : ManiaKeyMod
{
public override int KeyCount => 1;
public override string Name => "1K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey2 : ManiaKeyMod
{
public override int KeyCount => 2;
public override string Name => "2K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey3 : ManiaKeyMod
{
public override int KeyCount => 3;
public override string Name => "3K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey4 : ManiaKeyMod
{
public override int KeyCount => 4;
public override string Name => "4K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey5 : ManiaKeyMod
{
public override int KeyCount => 5;
public override string Name => "5K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey6 : ManiaKeyMod
{
public override int KeyCount => 6;
public override string Name => "6K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey7 : ManiaKeyMod
{
public override int KeyCount => 7;
public override string Name => "7K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey8 : ManiaKeyMod
{
public override int KeyCount => 8;
public override string Name => "8K";
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods
{
public class ManiaModKey9 : ManiaKeyMod
{
public override int KeyCount => 9;
public override string Name => "9K";
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModKeyCoop : Mod
{
public override string Name => "KeyCoop";
public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.0;
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModNoFail : ModNoFail
{
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModPerfect : ModPerfect
{
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModRandom : Mod, IApplicableToRulesetContainer<ManiaHitObject>
{
public override string Name => "Random";
public override string ShortenedName => "RD";
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
}
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModSuddenDeath : ModSuddenDeath
{
}
}

View File

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

View File

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

View File

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

View File

@ -78,6 +78,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
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)

View File

@ -5,16 +5,13 @@ using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Tests.Visual;
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("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
AddStep("Notes with input", () => createPlayfieldWithNotes());
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes());
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true));
AddStep("Hit explosion", () =>
{
@ -70,11 +67,6 @@ namespace osu.Game.Rulesets.Mania.Tests
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)
{
Clear();
@ -95,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Tests
return playfield;
}
private void createPlayfieldWithNotes(bool gravity, bool inverted = false)
private void createPlayfieldWithNotes(bool inverted = false)
{
Clear();
@ -114,23 +106,14 @@ namespace osu.Game.Rulesets.Mania.Tests
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)
{
if (gravity)
playfield.Columns.ElementAt(0).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 0
}, ManiaAction.Key1));
if (gravity)
playfield.Columns.ElementAt(3).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note
{
StartTime = t,
@ -138,9 +121,6 @@ namespace osu.Game.Rulesets.Mania.Tests
}, ManiaAction.Key4));
}
if (gravity)
playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
@ -148,9 +128,6 @@ namespace osu.Game.Rulesets.Mania.Tests
Column = 1
}, ManiaAction.Key2));
if (gravity)
playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.Tests
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new ManiaRuleset(new RulesetInfo()))
: base(new ManiaRuleset())
{
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,13 +16,12 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
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 SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
}
}

View File

@ -64,7 +64,30 @@
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Mods\IGenerateSpeedAdjustments.cs" />
<Compile Include="Mods\ManiaKeyMod.cs" />
<Compile Include="Mods\ManiaModAutoplay.cs" />
<Compile Include="Mods\ManiaModDaycore.cs" />
<Compile Include="Mods\ManiaModDoubleTime.cs" />
<Compile Include="Mods\ManiaModEasy.cs" />
<Compile Include="Mods\ManiaModFadeIn.cs" />
<Compile Include="Mods\ManiaModFlashlight.cs" />
<Compile Include="Mods\ManiaModHalfTime.cs" />
<Compile Include="Mods\ManiaModHardRock.cs" />
<Compile Include="Mods\ManiaModHidden.cs" />
<Compile Include="Mods\ManiaModKey1.cs" />
<Compile Include="Mods\ManiaModKey2.cs" />
<Compile Include="Mods\ManiaModKey3.cs" />
<Compile Include="Mods\ManiaModKey4.cs" />
<Compile Include="Mods\ManiaModKey5.cs" />
<Compile Include="Mods\ManiaModKey6.cs" />
<Compile Include="Mods\ManiaModKey7.cs" />
<Compile Include="Mods\ManiaModKey8.cs" />
<Compile Include="Mods\ManiaModKey9.cs" />
<Compile Include="Mods\ManiaModKeyCoop.cs" />
<Compile Include="Mods\ManiaModNightcore.cs" />
<Compile Include="Mods\ManiaModPerfect.cs" />
<Compile Include="Mods\ManiaModRandom.cs" />
<Compile Include="Mods\ManiaModSuddenDeath.cs" />
<Compile Include="Objects\Drawables\DrawableBarLine.cs" />
<Compile Include="Objects\Drawables\DrawableHoldNote.cs" />
<Compile Include="Objects\Drawables\DrawableHoldNoteTick.cs" />
@ -90,18 +113,14 @@
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
<Compile Include="Tests\TestCasePerformancePoints.cs" />
<Compile Include="Timing\GravityScrollingContainer.cs" />
<Compile Include="Timing\ScrollingAlgorithm.cs" />
<Compile Include="UI\Column.cs" />
<Compile Include="UI\DrawableManiaJudgement.cs" />
<Compile Include="UI\HitExplosion.cs" />
<Compile Include="UI\ManiaRulesetContainer.cs" />
<Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" />
<Compile Include="Mods\ManiaMod.cs" />
<Compile Include="Mods\ManiaModGravity.cs" />
<Compile Include="Mods\ManiaModNoFail.cs" />
<Compile Include="UI\SpecialColumnPosition.cs" />
<Compile Include="Timing\ManiaSpeedAdjustmentContainer.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />

View File

@ -5,7 +5,6 @@ using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Beatmaps
{
@ -29,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
}
obj.ComboIndex = comboIndex++;
obj.IndexInCurrentCombo = comboIndex++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
}
}
@ -37,7 +36,6 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
private void applyStacking(Beatmap<OsuHitObject> beatmap)
{
const int stack_distance = 3;
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
// Reset stacking
for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
@ -58,6 +56,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
if (objectN.StartTime - endTime > stackThreshold)
//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];
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.
* 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.

View File

@ -1,189 +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.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
using OpenTK;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModNoFail : ModNoFail
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
public class OsuModEasy : ModEasy
{
}
public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
{
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06;
private const double fade_in_duration_multiplier = 0.4;
private const double fade_out_duration_multiplier = 0.3;
private float preEmpt => DrawableOsuHitObject.TIME_PREEMPT;
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
{
d.ApplyCustomUpdateState += ApplyHiddenState;
d.FadeInDuration = preEmpt * fade_in_duration_multiplier;
}
}
protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
{
if (!(drawable is DrawableOsuHitObject d))
return;
var fadeOutStartTime = d.HitObject.StartTime - preEmpt + d.FadeInDuration;
var fadeOutDuration = preEmpt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out)
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;
switch (drawable)
{
case DrawableHitCircle circle:
// we don't want to see the approach circle
circle.ApproachCircle.Hide();
// fade out immediately after fade in.
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
circle.FadeOut(fadeOutDuration);
break;
case DrawableSlider slider:
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
slider.Body.FadeOut(longFadeDuration, Easing.Out);
break;
case DrawableSpinner spinner:
// hide elements we don't care about.
spinner.Disc.Hide();
spinner.Ticks.Hide();
spinner.Background.Hide();
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
spinner.FadeOut(fadeOutDuration);
break;
}
}
}
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
{
public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
public void ApplyToHitObject(OsuHitObject hitObject)
{
hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y);
var slider = hitObject as Slider;
if (slider == null)
return;
var newControlPoints = new List<Vector2>();
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve
}
}
public class OsuModSuddenDeath : ModSuddenDeath
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
public class OsuModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
}
public class OsuModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.12;
}
public class OsuModRelax : ModRelax
{
public override string Description => "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
public class OsuModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
}
public class OsuModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.12;
}
public class OsuModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.12;
}
public class OsuModPerfect : ModPerfect
{
}
public class OsuModSpunOut : Mod
{
public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed";
public override double ScoreMultiplier => 0.9;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) };
}
public class OsuModAutopilot : Mod
{
public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 0;
public override bool Ranked => false;
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
}
public class OsuModAutoplay : ModAutoplay<OsuHitObject>
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray();
protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap) => new Score
{
Replay = new OsuAutoGenerator(beatmap).Generate()
};
}
public class OsuModTarget : Mod
{
public override string Name => "Target";
public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"";
public override double ScoreMultiplier => 1;
}
}

View File

@ -0,0 +1,20 @@
// 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 osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModAutopilot : Mod
{
public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 0;
public override bool Ranked => false;
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModAutoplay : ModAutoplay<OsuHitObject>
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModSpunOut) }).ToArray();
protected override Score CreateReplayScore(Beatmap<OsuHitObject> beatmap)
{
return new Score
{
Replay = new OsuAutoGenerator(beatmap).Generate()
};
}
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.12;
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModEasy : ModEasy
{
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.12;
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
{
public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
public void ApplyToHitObject(OsuHitObject hitObject)
{
hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y);
var slider = hitObject as Slider;
if (slider == null)
return;
var newControlPoints = new List<Vector2>();
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve
}
}
}

View File

@ -0,0 +1,77 @@
// 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;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
{
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06;
private const float fade_in_duration_multiplier = 0.4f;
private const double fade_out_duration_multiplier = 0.3;
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
{
d.ApplyCustomUpdateState += ApplyHiddenState;
d.HitObject.TimeFadein = d.HitObject.TimePreempt * fade_in_duration_multiplier;
}
}
protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
{
if (!(drawable is DrawableOsuHitObject d))
return;
var fadeOutStartTime = d.HitObject.StartTime - d.HitObject.TimePreempt + d.HitObject.TimeFadein;
var fadeOutDuration = d.HitObject.TimePreempt * fade_out_duration_multiplier;
// new duration from completed fade in to end (before fading out)
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;
switch (drawable)
{
case DrawableHitCircle circle:
// we don't want to see the approach circle
circle.ApproachCircle.Hide();
// fade out immediately after fade in.
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
{
circle.FadeOut(fadeOutDuration);
}
break;
case DrawableSlider slider:
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
{
slider.Body.FadeOut(longFadeDuration, Easing.Out);
}
break;
case DrawableSpinner spinner:
// hide elements we don't care about.
spinner.Disc.Hide();
spinner.Ticks.Hide();
spinner.Background.Hide();
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
{
spinner.FadeOut(fadeOutDuration);
}
break;
}
}
}
}

View File

@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.12;
}
}

View File

@ -0,0 +1,14 @@
// 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.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModNoFail : ModNoFail
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
}

View File

@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModPerfect : ModPerfect
{
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModRelax : ModRelax
{
public override string Description => "You don't need to click.\nGive your clicking/tapping finger a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
}

View File

@ -0,0 +1,20 @@
// 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 osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModSpunOut : Mod
{
public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed";
public override double ScoreMultiplier => 0.9;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) };
}
}

View File

@ -0,0 +1,14 @@
// 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.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModSuddenDeath : ModSuddenDeath
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModTarget : Mod
{
public override string Name => "Target";
public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"";
public override double ScoreMultiplier => 1;
}
}

View File

@ -96,12 +96,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(DrawableOsuHitObject.TIME_FADEIN);
fp.ScaleTo(1, DrawableOsuHitObject.TIME_FADEIN, Easing.Out);
fp.FadeIn(currHitObject.TimeFadein);
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);

View File

@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
},
number = new NumberPiece
{
Text = (HitObject.ComboIndex + 1).ToString(),
Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
},
ring = new RingPiece(),
flash = new FlashPiece(),
@ -88,8 +88,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.UpdatePreemptState();
ApproachCircle.FadeIn(Math.Min(FadeInDuration * 2, TIME_PREEMPT));
ApproachCircle.ScaleTo(1.1f, TIME_PREEMPT);
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
}
protected override void UpdateCurrentState(ArmedState state)
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
switch (state)
{
case ArmedState.Idle:
this.Delay(TIME_PREEMPT).FadeOut(500);
this.Delay(HitObject.TimePreempt).FadeOut(500);
Expire(true);

View File

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

View File

@ -54,11 +54,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
StartTime = s.StartTime,
Position = s.StackedPosition,
ComboIndex = s.ComboIndex,
IndexInCurrentCombo = s.IndexInCurrentCombo,
Scale = s.Scale,
ComboColour = s.ComboColour,
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>())
{
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 drawableTick = new DrawableSliderTick(tick)
@ -88,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>())
{
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 drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
@ -106,12 +108,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private int currentRepeat;
public bool Tracking;
public override double FadeInDuration
{
get { return base.FadeInDuration; }
set { InitialCircle.FadeInDuration = base.FadeInDuration = value; }
}
protected override void Update()
{
base.Update();

View File

@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
if (!spmCounter.IsPresent && Disc.Tracking)
spmCounter.FadeIn(FadeInDuration);
spmCounter.FadeIn(HitObject.TimeFadein);
base.Update();
}
@ -191,14 +191,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.UpdatePreemptState();
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);
symbol.RotateTo(-720);
mainContainer
.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()
.ScaleTo(1, 500, Easing.OutQuint);
}

View File

@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public void UpdateProgress(double progress, int repeat)
{
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)
{

View File

@ -20,6 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects
private const double hit_window_100 = 80;
private const double hit_window_300 = 30;
public float TimePreempt = 600;
public float TimeFadein = 400;
public Vector2 Position { get; set; }
public float X => Position.X;
public float Y => Position.Y;
@ -40,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public Color4 ComboColour { get; set; } = Color4.Gray;
public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; }
public int IndexInCurrentCombo { get; set; }
public double HitWindowFor(HitResult result)
{
@ -72,6 +75,9 @@ namespace osu.Game.Rulesets.Osu.Objects
{
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;
}
}

View File

@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu
public override int LegacyID => 0;
public OsuRuleset(RulesetInfo rulesetInfo)
public OsuRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}

View File

@ -5,7 +5,6 @@ using OpenTK;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using System;
using System.Diagnostics;
using osu.Framework.Graphics;
@ -133,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Replays
// Do some nice easing for cursor movements
if (Frames.Count > 0)
{
moveToHitObject(h.StartTime, startPosition, h.Radius, easing);
moveToHitObject(h, startPosition, easing);
}
// 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];
// 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)
{
lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
@ -205,17 +204,17 @@ namespace osu.Game.Rulesets.Osu.Replays
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.
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.
{
// 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));
}

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new OsuRuleset(new RulesetInfo()))
: base(new OsuRuleset())
{
}
}

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