1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-26 20:01:20 +08:00

Compare commits

...

476 Commits

293 changed files with 6173 additions and 2963 deletions
+7 -5
View File
@@ -1,9 +1,11 @@
osu!lazer is currently in early stages of development and is not yet ready for end users. Please avoid creating issues or bugs if you do not personally intend to fix them. Some acceptable topics include:
osu!lazer is currently still under heavy development!
Please ensure that you are making an issue for one of the following:
- A bug with currently implemented features (not features that don't exist)
- A feature you are considering adding, so we can collaborate on feedback and design.
- Discussions about technical design decisions
- Bugs that you have found and are personally willing and able to fix
- TODO lists of smaller tasks around larger features
Basically, issues are not a place for you to get help. They are a place for developers to collaborate on the game.
If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide.
Screenshots and log files are highly welcomed.
+1 -1
View File
@@ -39,7 +39,7 @@ namespace osu.Desktop.Deploy
/// <summary>
/// How many previous build deltas we want to keep when publishing.
/// </summary>
private const int keep_delta_count = 3;
private const int keep_delta_count = 4;
private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\"";
+3 -5
View File
@@ -16,11 +16,9 @@
<language>en-AU</language>
</metadata>
<files>
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.config" target="lib\net45\"/>
<file src="x86\*.dll" target="lib\net45\x86\"/>
<file src="x64\*.dll" target="lib\net45\x64\"/>
<file src="**.exe" target="lib\net45\" exclude="**vshost**"/>
<file src="**.dll" target="lib\net45\"/>
<file src="**.config" target="lib\net45\"/>
</files>
</package>
@@ -16,29 +16,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
{
if (beatmap.ComboColors.Count == 0)
return;
int index = 0;
int colourIndex = 0;
CatchHitObject lastObj = null;
initialiseHyperDash(beatmap.HitObjects);
base.PostProcess(beatmap);
int index = 0;
foreach (var obj in beatmap.HitObjects)
{
if (obj.NewCombo)
{
if (lastObj != null) lastObj.LastInCombo = true;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
}
obj.IndexInBeatmap = index++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
lastObj = obj;
}
}
private void initialiseHyperDash(List<CatchHitObject> objects)
+1 -1
View File
@@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Catch
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override int LegacyID => 2;
public override int? LegacyID => 2;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
public override double ScoreMultiplier => 0.3;
}
}
@@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModEasy : ModEasy
{
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
}
}
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
public override double ScoreMultiplier => 0.3;
}
}
@@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods
public class CatchModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
}
}
@@ -7,7 +7,7 @@ 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 string Description => @"Play with fading fruits.";
public override double ScoreMultiplier => 1.06;
}
}
@@ -3,7 +3,6 @@
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects
{
@@ -32,25 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new Banana
{
Samples = Samples,
ComboColour = getNextComboColour(),
StartTime = i,
X = RNG.NextSingle()
});
}
private Color4 getNextComboColour()
{
switch (RNG.Next(0, 3))
{
default:
return new Color4(255, 240, 0, 255);
case 1:
return new Color4(255, 192, 0, 255);
case 2:
return new Color4(214, 221, 28, 255);
}
}
public double EndTime => StartTime + Duration;
public double Duration { get; set; }
@@ -5,24 +5,25 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects
{
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
{
public const double OBJECT_RADIUS = 44;
public float X { 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; }
public int IndexInCurrentCombo { get; set; }
public int ComboIndex { get; set; }
/// <summary>
/// The next fruit starts a new combo. Used for explodey.
/// </summary>
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Origin = Anchor.BottomLeft;
X = 0;
Child = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
AddNested(getVisualRepresentation?.Invoke(b));
@@ -8,6 +8,8 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
@@ -57,6 +59,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
private const float preempt = 1000;
protected override void UpdateState(ArmedState state)
@@ -5,28 +5,39 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
{
private Pulp pulp;
public DrawableDroplet(Droplet h)
: base(h)
{
Origin = Anchor.Centre;
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
AccentColour = h.ComboColour;
Masking = false;
}
[BackgroundDependencyLoader]
private void load()
{
Child = new Pulp
InternalChild = pulp = new Pulp
{
AccentColour = AccentColour,
Size = Size
};
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
base.AccentColour = value;
pulp.AccentColour = AccentColour;
}
}
}
}
@@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Origin = Anchor.Centre;
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
AccentColour = HitObject.ComboColour;
Masking = false;
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
@@ -33,7 +32,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
[BackgroundDependencyLoader]
private void load()
{
Children = new[]
// todo: this should come from the skin.
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
InternalChildren = new[]
{
createPulp(HitObject.VisualRepresentation),
border = new Circle
@@ -65,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
if (HitObject.HyperDash)
{
Add(new Pulp
AddInternal(new Pulp
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.Centre,
@@ -273,5 +275,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
}
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);
}
}
}
}
}
@@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Origin = Anchor.BottomLeft;
X = 0;
Child = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
AddNested(getVisualRepresentation?.Invoke(o));
@@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
var catchObject = (DrawableCatchHitObject)h;
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
catchObject.AccentColour = HitObject.ComboColour;
dropletContainer.Add(h);
base.AddNested(h);
+38 -37
View File
@@ -60,65 +60,66 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new Fruit
{
Samples = Samples,
ComboColour = ComboColour,
StartTime = StartTime,
X = X
});
for (var span = 0; span < this.SpanCount(); span++)
double lastDropletTime = StartTime;
for (int span = 0; span < this.SpanCount(); span++)
{
var spanStartTime = StartTime + span * spanDuration;
var reversed = span % 2 == 1;
for (var d = tickDistance; d <= length; d += tickDistance)
for (double d = 0; d <= length; d += tickDistance)
{
if (d > length - minDistanceFromEnd)
break;
var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
var lastTickTime = spanStartTime + timeProgress * spanDuration;
AddNested(new Droplet
double time = spanStartTime + timeProgress * spanDuration;
double tinyTickInterval = time - lastDropletTime;
while (tinyTickInterval > 100)
tinyTickInterval /= 2;
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
{
StartTime = lastTickTime,
ComboColour = ComboColour,
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
AddNested(new TinyDroplet
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
});
}
StartTime = t,
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
});
}
double tinyTickInterval = tickDistance / length * spanDuration;
while (tinyTickInterval > 100)
tinyTickInterval /= 2;
for (double t = 0; t < spanDuration; t += tinyTickInterval)
{
double progress = reversed ? 1 - t / spanDuration : t / spanDuration;
AddNested(new TinyDroplet
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
{
StartTime = spanStartTime + t,
ComboColour = ComboColour,
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
AddNested(new Droplet
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
});
StartTime = time,
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
});
}
lastDropletTime = time;
}
AddNested(new Fruit
{
Samples = Samples,
ComboColour = ComboColour,
StartTime = spanStartTime + spanDuration,
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
});
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2149")]
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
public new void Test(string name)
{
base.Test(name);
@@ -28,16 +28,14 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
protected override Beatmap CreateBeatmap()
protected override Beatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 6,
}
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
}
};
@@ -15,19 +15,18 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
protected override Beatmap CreateBeatmap()
protected override Beatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 6,
}
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
}
};
for (int i = 0; i < 512; i++)
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
@@ -6,13 +6,11 @@ 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
{
@@ -62,8 +60,6 @@ namespace osu.Game.Rulesets.Catch.Tests
Scale = 1.5f,
};
fruit.ComboColour = colourForRrepesentation(fruit.VisualRepresentation);
return new DrawableFruit(fruit)
{
Anchor = Anchor.Centre,
@@ -74,31 +70,5 @@ namespace osu.Game.Rulesets.Catch.Tests
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);
}
}
}
}
}
@@ -15,9 +15,10 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
protected override Beatmap CreateBeatmap()
protected override Beatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap();
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++)
if (i % 5 < 3)
@@ -54,7 +54,6 @@ namespace osu.Game.Rulesets.Catch.UI
if (caughtFruit == null) return;
caughtFruit.AccentColour = fruit.AccentColour;
caughtFruit.RelativePositionAxes = Axes.None;
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
@@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
yield break;
}
var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original);
var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
if (objects == null)
yield break;
@@ -110,10 +110,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for osu!mania specific beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap)
{
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
@@ -125,26 +126,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for non-osu!mania beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateConverted(HitObject original)
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap)
{
var endTimeData = original as IHasEndTime;
var distanceData = original as IHasDistance;
var positionData = original as IHasPosition;
// Following lines currently commented out to appease resharper
Patterns.PatternGenerator conversion = null;
if (distanceData != null)
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
else if (endTimeData != null)
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
else if (positionData != null)
{
computeDensity(original.StartTime);
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
recordNote(original.StartTime, positionData.Position);
}
@@ -153,10 +153,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return null;
Pattern newPattern = conversion.Generate();
lastPattern = newPattern;
var stairPatternGenerator = conversion as HitObjectPatternGenerator;
lastStair = stairPatternGenerator?.StairType ?? lastStair;
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
return newPattern.HitObjects;
}
@@ -166,8 +165,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern)
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
}
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@@ -29,11 +30,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType;
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern)
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
convertType = PatternType.LowProbability;
var distanceData = hitObject as IHasDistance;
@@ -305,19 +306,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p4 = 0;
break;
case 3:
p2 = Math.Max(p2, 0.1);
p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
break;
case 4:
p2 = Math.Max(p2, 0.3);
p3 = Math.Max(p3, 0.04);
p2 = Math.Min(p2, 0.3);
p3 = Math.Min(p3, 0.04);
p4 = 0;
break;
case 5:
p2 = Math.Max(p2, 0.34);
p3 = Math.Max(p3, 0.1);
p4 = Math.Max(p4, 0.03);
p2 = Math.Min(p2, 0.34);
p3 = Math.Min(p3, 0.1);
p4 = Math.Min(p4, 0.03);
break;
}
@@ -396,17 +397,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Create the hold note
addToPattern(pattern, holdColumn, startTime, endTime);
int noteCount = 1;
int nextColumn = Random.Next(RandomStart, TotalColumns);
int noteCount;
if (ConversionDifficulty > 6.5)
noteCount = GetRandomNoteCount(0.63, 0);
else if (ConversionDifficulty > 4)
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0);
else if (ConversionDifficulty > 2.5)
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0);
else
noteCount = 0;
noteCount = Math.Min(TotalColumns - 1, noteCount);
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
int nextColumn = Random.Next(RandomStart, TotalColumns);
var rowPattern = new Pattern();
for (int i = 0; i <= spanCount; i++)
@@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
@@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
private readonly double endTime;
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap)
: base(random, hitObject, beatmap, new Pattern())
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{
var endtimeData = HitObject as IHasEndTime;
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using OpenTK;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Mania.Objects;
@@ -19,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
: base(random, hitObject, beatmap, previousPattern)
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
@@ -308,20 +309,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p5 = 0;
break;
case 3:
p2 = Math.Max(p2, 0.1);
p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
p5 = 0;
break;
case 4:
p2 = Math.Max(p2, 0.23);
p3 = Math.Max(p3, 0.04);
p2 = Math.Min(p2, 0.23);
p3 = Math.Min(p3, 0.04);
p4 = 0;
p5 = 0;
break;
case 5:
p3 = Math.Max(p3, 0.15);
p4 = Math.Max(p4, 0.03);
p3 = Math.Min(p3, 0.15);
p4 = Math.Min(p4, 0.03);
p5 = 0;
break;
}
@@ -355,23 +356,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p3 = 0;
break;
case 3:
centreProbability = Math.Max(centreProbability, 0.03);
p2 = Math.Max(p2, 0.1);
centreProbability = Math.Min(centreProbability, 0.03);
p2 = 0;
p3 = 0;
break;
case 4:
centreProbability = 0;
p2 = Math.Max(p2 * 2, 0.2);
p2 = Math.Min(p2 * 2, 0.2);
p3 = 0;
break;
case 5:
centreProbability = Math.Max(centreProbability, 0.03);
centreProbability = Math.Min(centreProbability, 0.03);
p3 = 0;
break;
case 6:
centreProbability = 0;
p2 = Math.Max(p2 * 2, 0.5);
p3 = Math.Max(p3 * 2, 0.15);
p2 = Math.Min(p2 * 2, 0.5);
p3 = Math.Min(p3 * 2, 0.15);
break;
}
@@ -25,14 +25,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary>
protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
/// <summary>
/// The beatmap which <see cref="HitObject"/> is being converted from.
/// </summary>
protected readonly Beatmap OriginalBeatmap;
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(hitObject, beatmap, previousPattern)
{
if (random == null) throw new ArgumentNullException(nameof(random));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap));
Random = random;
OriginalBeatmap = originalBeatmap;
RandomStart = TotalColumns == 8 ? 1 : 0;
}
@@ -94,17 +100,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (conversionDifficulty != null)
return conversionDifficulty.Value;
HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
drainTime -= Beatmap.TotalBreakTime;
drainTime -= OriginalBeatmap.TotalBreakTime;
if (drainTime == 0)
drainTime = 10000;
drainTime = 10000000;
BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BaseDifficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
// We need this in seconds
drainTime /= 1000;
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value;
@@ -4,18 +4,142 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania
{
public class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
{
private const double star_scaling_factor = 0.018;
/// <summary>
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step.
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
/// </summary>
private const double strain_step = 400;
/// <summary>
/// The weighting of each strain value decays to this number * it's previous value
/// </summary>
private const double decay_weight = 0.9;
/// <summary>
/// HitObjects are stored as a member variable.
/// </summary>
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
public ManiaDifficultyCalculator(Beatmap beatmap)
: base(beatmap)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods)
: base(beatmap, mods)
{
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
foreach (var hitObject in Beatmap.HitObjects)
difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
if (!calculateStrainValues())
return 0;
double starRating = calculateDifficulty() * star_scaling_factor;
categoryDifficulty?.Add("Strain", starRating);
return starRating;
}
private bool calculateStrainValues()
{
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
using (List<ManiaHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
{
if (!hitObjectsEnumerator.MoveNext())
return false;
ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current;
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
current = next;
}
return true;
}
}
private double calculateDifficulty()
{
double actualStrainStep = strain_step * TimeRate;
// Find the highest strain value within each strain step
List<double> highestStrains = new List<double>();
double intervalEndTime = actualStrainStep;
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
ManiaHitObjectDifficulty previousHitObject = null;
foreach (var hitObject in difficultyHitObjects)
{
// While we are beyond the current interval push the currently available maximum to our strain list
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
{
highestStrains.Add(maximumStrain);
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
// until the beginning of the next interval.
if (previousHitObject == null)
{
maximumStrain = 0;
}
else
{
double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay;
}
// Go to the next time interval
intervalEndTime += actualStrainStep;
}
// Obtain maximum strain
double strain = hitObject.IndividualStrain + hitObject.OverallStrain;
maximumStrain = Math.Max(strain, maximumStrain);
previousHitObject = hitObject;
}
// Build the weighted sum over the highest strains for each interval
double difficulty = 0;
double weight = 1;
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
foreach (double strain in highestStrains)
{
difficulty += weight * strain;
weight *= decay_weight;
}
return difficulty;
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap);
}
+3 -2
View File
@@ -91,6 +91,7 @@ namespace osu.Game.Rulesets.Mania
},
new ManiaModRandom(),
new ManiaModDualStages(),
new ManiaModMirror(),
new MultiMod
{
Mods = new Mod[]
@@ -112,9 +113,9 @@ namespace osu.Game.Rulesets.Mania
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
public override int LegacyID => 3;
public override int? LegacyID => 3;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.3;
public override double ScoreMultiplier => 0.5;
}
}
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.0;
public override double ScoreMultiplier => 1;
}
}
@@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Dual Stages";
public override string ShortenedName => "DS";
public override string Description => @"Double the stages, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => false;
public override double ScoreMultiplier => 0;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
{
@@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModEasy : ModEasy
{
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
}
}
@@ -9,10 +9,11 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModFadeIn : Mod
{
public override string Name => "FadeIn";
public override string Name => "Fade In";
public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.0;
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
}
}
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.3;
public override double ScoreMultiplier => 0.5;
}
}
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.0;
public override double ScoreMultiplier => 1;
}
}
@@ -8,8 +8,8 @@ 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 string Description => @"Keys fade out before you hit them!";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey1 : ManiaKeyMod
{
public override int KeyCount => 1;
public override string Name => "1K";
public override string Name => "One Key";
public override string ShortenedName => "1K";
public override string Description => @"Play with one key.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey2 : ManiaKeyMod
{
public override int KeyCount => 2;
public override string Name => "2K";
public override string Name => "Two Keys";
public override string ShortenedName => "2K";
public override string Description => @"Play with two keys.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey3 : ManiaKeyMod
{
public override int KeyCount => 3;
public override string Name => "3K";
public override string Name => "Three Keys";
public override string ShortenedName => "3K";
public override string Description => @"Play with three keys.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey4 : ManiaKeyMod
{
public override int KeyCount => 4;
public override string Name => "4K";
public override string Name => "Four Keys";
public override string ShortenedName => "4K";
public override string Description => @"Play with four keys.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey5 : ManiaKeyMod
{
public override int KeyCount => 5;
public override string Name => "5K";
public override string Name => "Five Keys";
public override string ShortenedName => "5K";
public override string Description => @"Play with five keys.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey6 : ManiaKeyMod
{
public override int KeyCount => 6;
public override string Name => "6K";
public override string Name => "Six Keys";
public override string ShortenedName => "6K";
public override string Description => @"Play with six keys.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey7 : ManiaKeyMod
{
public override int KeyCount => 7;
public override string Name => "7K";
public override string Name => "Seven Keys";
public override string ShortenedName => "7K";
public override string Description => @"Play with seven keys.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey8 : ManiaKeyMod
{
public override int KeyCount => 8;
public override string Name => "8K";
public override string Name => "Eight Keys";
public override string ShortenedName => "8K";
public override string Description => @"Play with eight keys.";
}
}
+3 -1
View File
@@ -6,6 +6,8 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKey9 : ManiaKeyMod
{
public override int KeyCount => 9;
public override string Name => "9K";
public override string Name => "Nine Keys";
public override string ShortenedName => "9K";
public override string Description => @"Play with nine keys.";
}
}
@@ -0,0 +1,28 @@
// 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.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Linq;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModMirror : Mod, IApplicableToRulesetContainer<ManiaHitObject>
{
public override string Name => "Mirror";
public override string ShortenedName => "MR";
public override ModType Type => ModType.Special;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns;
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
}
}
}
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.0;
public override double ScoreMultiplier => 1;
}
}
@@ -17,8 +17,8 @@ namespace osu.Game.Rulesets.Mania.Mods
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 override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 0;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X;
Height = 1;
Add(new Box
AddInternal(new Box
{
Name = "Bar line",
Anchor = Anchor.BottomCentre,
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (isMajor)
{
Add(new EquilateralTriangle
AddInternal(new EquilateralTriangle
{
Name = "Left triangle",
Anchor = Anchor.BottomLeft,
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Rotation = 90
});
Add(new EquilateralTriangle
AddInternal(new EquilateralTriangle
{
Name = "Right triangle",
Anchor = Anchor.BottomRight,
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring;
@@ -24,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
private readonly GlowPiece glowPiece;
private readonly BodyPiece bodyPiece;
private readonly Container<DrawableHoldNoteTick> tickContainer;
private readonly Container fullHeightContainer;
/// <summary>
@@ -40,9 +38,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
: base(hitObject, action)
{
Container<DrawableHoldNoteTick> tickContainer;
RelativeSizeAxes = Axes.X;
AddRange(new Drawable[]
InternalChildren = new Drawable[]
{
// The hit object itself cannot be used for various elements because the tail overshoots it
// So a specialized container that is updated to contain the tail height is used
@@ -57,7 +56,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
},
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
tickContainer = new Container<DrawableHoldNoteTick>
{
RelativeSizeAxes = Axes.Both,
ChildrenEnumerable = HitObject.NestedHitObjects.OfType<HoldNoteTick>().Select(tick => new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => holdStartTime
})
},
head = new DrawableHeadNote(this, action)
{
Anchor = Anchor.TopCentre,
@@ -68,18 +74,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
});
};
foreach (var tick in HitObject.NestedHitObjects.OfType<HoldNoteTick>())
{
var drawableTick = new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => holdStartTime
};
tickContainer.Add(drawableTick);
AddNested(drawableTick);
}
foreach (var tick in tickContainer)
AddNested(tick);
AddNested(head);
AddNested(tail);
@@ -90,12 +88,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
tickContainer.Children.ForEach(t => t.AccentColour = value);
glowPiece.AccentColour = value;
bodyPiece.AccentColour = value;
head.AccentColour = value;
@@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X;
Size = new Vector2(1);
Children = new[]
InternalChildren = new[]
{
glowContainer = new CircularContainer
{
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using OpenTK.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
@@ -28,16 +27,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != null)
Action = action.Value;
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
}
}
}
}
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
laneGlowPiece = new LaneGlowPiece
{
@@ -48,13 +48,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
get { return base.AccentColour; }
set
{
if (base.AccentColour == value)
return;
base.AccentColour = value;
laneGlowPiece.AccentColour = value;
GlowPiece.AccentColour = value;
headPiece.AccentColour = value;
laneGlowPiece.AccentColour = AccentColour;
GlowPiece.AccentColour = AccentColour;
headPiece.AccentColour = AccentColour;
}
}
@@ -0,0 +1,113 @@
// 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.Objects.Types;
using System;
namespace osu.Game.Rulesets.Mania.Objects
{
internal class ManiaHitObjectDifficulty
{
/// <summary>
/// Factor by how much individual / overall strain decays per second.
/// </summary>
/// <remarks>
/// These values are results of tweaking a lot and taking into account general feedback.
/// </remarks>
internal const double INDIVIDUAL_DECAY_BASE = 0.125;
internal const double OVERALL_DECAY_BASE = 0.30;
internal ManiaHitObject BaseHitObject;
private readonly int beatmapColumnCount;
private readonly double endTime;
private readonly double[] heldUntil;
/// <summary>
/// Measures jacks or more generally: repeated presses of the same button
/// </summary>
private readonly double[] individualStrains;
internal double IndividualStrain
{
get
{
return individualStrains[BaseHitObject.Column];
}
set
{
individualStrains[BaseHitObject.Column] = value;
}
}
/// <summary>
/// Measures note density in a way
/// </summary>
internal double OverallStrain = 1;
public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount)
{
BaseHitObject = baseHitObject;
endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime;
beatmapColumnCount = columnCount;
heldUntil = new double[beatmapColumnCount];
individualStrains = new double[beatmapColumnCount];
for (int i = 0; i < beatmapColumnCount; ++i)
{
individualStrains[i] = 0;
heldUntil[i] = 0;
}
}
internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate)
{
// TODO: Factor in holds
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000);
double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000);
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
// Fill up the heldUntil array
for (int i = 0; i < beatmapColumnCount; ++i)
{
heldUntil[i] = previousHitObject.heldUntil[i];
// If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i])
{
holdAddition = 1.0;
}
// ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1
if (endTime == heldUntil[i])
{
holdAddition = 0;
}
// We give a slight bonus to everything if something is held meanwhile
if (heldUntil[i] > endTime)
{
holdFactor = 1.25;
}
// Decay individual strains
individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay;
}
heldUntil[BaseHitObject.Column] = endTime;
// Increase individual strain in own column
IndividualStrain += 2.0 * holdFactor;
OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor;
}
}
}
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Tests
private bool isForCurrentRuleset;
[NonParallelizable]
[TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2150")]
[TestCase("basic", false)]
public void Test(string name, bool isForCurrentRuleset)
{
this.isForCurrentRuleset = isForCurrentRuleset;
@@ -1,17 +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
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
internal class DrawableManiaJudgement : DrawableJudgement
{
public DrawableManiaJudgement(Judgement judgement)
: base(judgement)
public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
JudgementText.TextSize = 25;
}
[BackgroundDependencyLoader]
private void load()
{
if (JudgementText != null)
JudgementText.TextSize = 25;
}
protected override void LoadComplete()
+4 -3
View File
@@ -15,6 +15,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
using OpenTK.Graphics;
@@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Container<Drawable> content;
public Container<DrawableManiaJudgement> Judgements => judgements;
private readonly Container<DrawableManiaJudgement> judgements;
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
private readonly Container topLevelContainer;
@@ -114,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.UI
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
}
},
judgements = new Container<DrawableManiaJudgement>
judgements = new JudgementContainer<DrawableManiaJudgement>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
@@ -171,7 +172,7 @@ namespace osu.Game.Rulesets.Mania.UI
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement)
judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -81,6 +81,7 @@
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Mods\IPlayfieldTypeMod.cs" />
<Compile Include="Mods\ManiaModMirror.cs" />
<Compile Include="Mods\ManiaKeyMod.cs" />
<Compile Include="Mods\ManiaModAutoplay.cs" />
<Compile Include="Mods\ManiaModDaycore.cs" />
@@ -114,6 +115,7 @@
<Compile Include="Objects\Drawables\Pieces\GlowPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\LaneGlowPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
<Compile Include="Objects\ManiaHitObjectDifficulty.cs" />
<Compile Include="Objects\Types\IHasColumn.cs" />
<Compile Include="Replays\ManiaAutoGenerator.cs" />
<Compile Include="Replays\ManiaFramedReplayInputHandler.cs" />
@@ -13,24 +13,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
public override void PostProcess(Beatmap<OsuHitObject> beatmap)
{
applyStacking(beatmap);
if (beatmap.ComboColors.Count == 0)
return;
int comboIndex = 0;
int colourIndex = 0;
foreach (var obj in beatmap.HitObjects)
{
if (obj.NewCombo)
{
comboIndex = 0;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
}
obj.IndexInCurrentCombo = comboIndex++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
}
base.PostProcess(beatmap);
}
private void applyStacking(Beatmap<OsuHitObject> beatmap)
@@ -1,26 +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.Edit.Layers.Selection;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection
{
public class OsuHitObjectOverlayLayer : HitObjectOverlayLayer
{
protected override HitObjectOverlay CreateOverlayFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleOverlay(circle);
case DrawableSlider slider:
return new SliderOverlay(slider);
}
return base.CreateOverlayFor(hitObject);
}
}
}
@@ -4,15 +4,15 @@
using osu.Framework.Graphics;
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class HitCircleOverlay : HitObjectOverlay
public class HitCircleMask : HitObjectMask
{
public HitCircleOverlay(DrawableHitCircle hitCircle)
public HitCircleMask(DrawableHitCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
@@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
Scale = hitCircle.Scale;
AddInternal(new RingPiece());
hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position;
}
[BackgroundDependencyLoader]
@@ -4,28 +4,29 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class SliderCircleOverlay : HitObjectOverlay
public class SliderCircleMask : HitObjectMask
{
public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider)
: this(sliderHead, sliderHead.Position, slider)
public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider)
: this(sliderHead, Vector2.Zero, slider)
{
}
public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider)
: this(sliderTail, sliderTail.Position, slider)
public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider)
: this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider)
{
}
private readonly DrawableOsuHitObject hitObject;
private SliderCircleOverlay(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
: base(hitObject)
{
this.hitObject = hitObject;
@@ -4,36 +4,41 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
public class SliderOverlay : HitObjectOverlay
public class SliderMask : HitObjectMask
{
private readonly SliderBody body;
private readonly DrawableSlider slider;
public SliderOverlay(DrawableSlider slider)
public SliderMask(DrawableSlider slider)
: base(slider)
{
this.slider = slider;
var obj = (Slider)slider.HitObject;
Position = slider.Position;
var sliderObject = (Slider)slider.HitObject;
InternalChildren = new Drawable[]
{
body = new SliderBody(obj)
body = new SliderBody(sliderObject)
{
AccentColour = Color4.Transparent,
PathWidth = obj.Scale * 64
PathWidth = sliderObject.Scale * 64
},
new SliderCircleOverlay(slider.HeadCircle, slider),
new SliderCircleOverlay(slider.TailCircle, slider),
new SliderCircleMask(slider.HeadCircle, slider),
new SliderCircleMask(slider.TailCircle, slider),
};
sliderObject.PositionChanged += _ => Position = slider.Position;
}
[BackgroundDependencyLoader]
@@ -46,12 +51,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
{
base.Update();
Position = slider.Position;
Size = slider.Size;
OriginPosition = slider.OriginPosition;
// Need to cause one update
body.UpdateProgress(0);
}
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos);
}
}
@@ -5,10 +5,11 @@ using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Layers.Selection;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
@@ -32,6 +33,17 @@ namespace osu.Game.Rulesets.Osu.Edit
protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
protected override HitObjectOverlayLayer CreateHitObjectOverlayLayer() => new OsuHitObjectOverlayLayer();
public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleMask(circle);
case DrawableSlider slider:
return new SliderMask(slider);
}
return base.CreateMaskFor(hitObject);
}
}
}
+1 -1
View File
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
public override double ScoreMultiplier => 0.3;
}
}
+1
View File
@@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModEasy : ModEasy
{
public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!";
}
}
+1 -1
View File
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
public override double ScoreMultiplier => 0.3;
}
}
@@ -14,7 +14,6 @@ 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)
{
+1 -1
View File
@@ -15,7 +15,7 @@ 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 string Description => @"Play with no approach circles and fading circles/sliders.";
public override double ScoreMultiplier => 1.06;
private const double fade_in_duration_multiplier = 0.4;
+1 -1
View File
@@ -9,7 +9,7 @@ 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 string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
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 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) };
+1 -1
View File
@@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
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 string Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1;
}
}
@@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -21,22 +22,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly NumberPiece number;
private readonly GlowPiece glow;
public DrawableHitCircle(HitCircle h) : base(h)
public DrawableHitCircle(HitCircle h)
: base(h)
{
Origin = Anchor.Centre;
Position = HitObject.StackedPosition;
Scale = new Vector2(h.Scale);
Children = new Drawable[]
InternalChildren = new Drawable[]
{
glow = new GlowPiece
{
Colour = AccentColour
},
glow = new GlowPiece(),
circle = new CirclePiece
{
Colour = AccentColour,
Hit = () =>
{
if (AllJudged)
@@ -52,20 +50,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
},
ring = new RingPiece(),
flash = new FlashPiece(),
explode = new ExplodePiece
{
Colour = AccentColour,
},
explode = new ExplodePiece(),
ApproachCircle = new ApproachCircle
{
Alpha = 0,
Scale = new Vector2(4),
Colour = AccentColour,
}
};
//may not be so correct
Size = circle.DrawSize;
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
base.AccentColour = value;
explode.Colour = AccentColour;
glow.Colour = AccentColour;
circle.Colour = AccentColour;
ApproachCircle.Colour = AccentColour;
}
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
@@ -5,6 +5,9 @@ using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using System.Linq;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -15,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
AccentColour = HitObject.ComboColour;
Alpha = 0;
}
@@ -35,6 +37,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
protected virtual void UpdateCurrentState(ArmedState state)
@@ -2,24 +2,24 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using OpenTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement
{
public DrawableOsuJudgement(OsuJudgement judgement)
: base(judgement)
public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
}
protected override void LoadComplete()
{
if (Judgement.Result != HitResult.Miss)
JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();
}
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new SpriteIcon
{
@@ -12,8 +12,8 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics.Primitives;
using osu.Game.Configuration;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -38,11 +38,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Container<DrawableSliderTick> ticks;
Container<DrawableRepeatPoint> repeatPoints;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
Body = new SliderBody(s)
{
AccentColour = AccentColour,
PathWidth = s.Scale * 64,
},
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
@@ -51,12 +50,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
BypassAutoSizeAxes = Axes.Both,
Scale = new Vector2(s.Scale),
AccentColour = AccentColour,
AlwaysPresent = true,
Alpha = 0
},
HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.TailCircle.Position - s.Position },
TailCircle = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position }
HeadCircle = new DrawableSliderHead(s, s.HeadCircle),
TailCircle = new DrawableSliderTail(s, s.TailCircle)
};
components.Add(Body);
@@ -84,6 +82,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
components.Add(drawableRepeatPoint);
AddNested(drawableRepeatPoint);
}
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
base.AccentColour = value;
Body.AccentColour = AccentColour;
Ball.AccentColour = AccentColour;
}
}
[BackgroundDependencyLoader]
@@ -103,10 +114,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!HeadCircle.IsHit)
HeadCircle.Position = slider.CurvePositionAt(completionProgress);
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(completionProgress);
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
foreach (var t in components.OfType<IRequireTracking>()) t.Tracking = Ball.Tracking;
@@ -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 osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSliderHead : DrawableHitCircle
{
private readonly Slider slider;
public DrawableSliderHead(Slider slider, HitCircle h)
: base(h)
{
this.slider = slider;
Position = HitObject.Position - slider.Position;
}
protected override void Update()
{
base.Update();
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!IsHit)
Position = slider.CurvePositionAt(completionProgress);
}
}
}
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
public DrawableSliderTail(HitCircle hitCircle)
public DrawableSliderTail(Slider slider, HitCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
@@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
FillMode = FillMode.Fit;
AlwaysPresent = true;
Position = HitObject.Position - slider.Position;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
@@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
BorderThickness = 2;
BorderColour = Color4.White;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
new Box
{
@@ -50,10 +50,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdatePreemptState()
{
this.Animate(
d => d.FadeIn(ANIM_DURATION),
d => d.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf)
);
this.FadeOut().FadeIn(ANIM_DURATION);
this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf);
}
protected override void UpdateCurrentState(ArmedState state)
@@ -64,12 +62,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
this.Delay(HitObject.TimePreempt).FadeOut();
break;
case ArmedState.Miss:
this.FadeOut(ANIM_DURATION)
.FadeColour(Color4.Red, ANIM_DURATION / 2);
this.FadeOut(ANIM_DURATION);
this.FadeColour(Color4.Red, ANIM_DURATION / 2);
break;
case ArmedState.Hit:
this.FadeOut(ANIM_DURATION, Easing.OutQuint)
.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out);
this.FadeOut(ANIM_DURATION, Easing.OutQuint);
this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out);
break;
}
}
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Spinner = s;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
circleContainer = new Container
{
@@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Skinning;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@@ -19,15 +20,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]
Child = new SkinnableDrawable("Play/osu/hitcircle-explode", _ => new TrianglesPiece
{
new TrianglesPiece
{
Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
}
};
Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
}, s => s.GetTexture("Play/osu/hitcircle") == null);
}
}
}
@@ -5,6 +5,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
using osu.Framework.Graphics.Shapes;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -14,22 +15,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Size = new Vector2(128);
Masking = true;
CornerRadius = Size.X / 2;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]
Child = new SkinnableDrawable("Play/osu/hitcircle-flash", name => new CircularContainer
{
new Box
Masking = true,
RelativeSizeAxes = Axes.Both,
Child = new Box
{
RelativeSizeAxes = Axes.Both
}
};
}, s => s.GetTexture("Play/osu/hitcircle") == null);
}
}
}
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Texture = textures.Get(name),
Blending = BlendingMode.Additive,
Alpha = 0.5f
}, false);
}, s => s.GetTexture("Play/osu/hitcircle") == null);
}
}
}
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Colour = Color4.White.Opacity(0.5f),
},
Child = new Box()
}, false),
}, s => s.GetTexture("Play/osu/hitcircle") == null),
number = new OsuSpriteText
{
Text = @"1",
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
private Color4 accentColour;
private Color4 accentColour = Color4.White;
/// <summary>
/// Used to colour the path.
/// </summary>
@@ -173,6 +173,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
texture.SetData(upload);
path.Texture = texture;
container.ForceRedraw();
}
private void computeSize()
+26 -4
View File
@@ -1,23 +1,39 @@
// 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.Beatmaps;
using osu.Game.Rulesets.Objects;
using OpenTK;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects
{
public abstract class OsuHitObject : HitObject, IHasCombo, IHasPosition
public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition
{
public const double OBJECT_RADIUS = 64;
public event Action<Vector2> PositionChanged;
public double TimePreempt = 600;
public double TimeFadein = 400;
public Vector2 Position { get; set; }
private Vector2 position;
public Vector2 Position
{
get => position;
set
{
if (position == value)
return;
position = value;
PositionChanged?.Invoke(value);
}
}
public float X => Position.X;
public float Y => Position.Y;
@@ -35,10 +51,14 @@ namespace osu.Game.Rulesets.Osu.Objects
public float Scale { get; set; } = 1;
public Color4 ComboColour { get; set; } = Color4.Gray;
public virtual bool NewCombo { get; set; }
public int IndexInCurrentCombo { get; set; }
public int ComboIndex { get; set; }
public bool LastInCombo { get; set; }
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
@@ -48,5 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
public virtual void OffsetPosition(Vector2 offset) => Position += offset;
}
}
+6 -8
View File
@@ -95,22 +95,22 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createSliderEnds()
{
HeadCircle = new HitCircle
HeadCircle = new SliderCircle(this)
{
StartTime = StartTime,
Position = Position,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboColour = ComboColour,
Samples = Samples,
SampleControlPoint = SampleControlPoint
SampleControlPoint = SampleControlPoint,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboIndex = ComboIndex,
};
TailCircle = new HitCircle
TailCircle = new SliderCircle(this)
{
StartTime = EndTime,
Position = EndPosition,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboColour = ComboColour
ComboIndex = ComboIndex,
};
AddNested(HeadCircle);
@@ -160,7 +160,6 @@ namespace osu.Game.Rulesets.Osu.Objects
Position = Position + Curve.PositionAt(distanceProgress),
StackHeight = StackHeight,
Scale = Scale,
ComboColour = ComboColour,
Samples = sampleList
});
}
@@ -179,7 +178,6 @@ namespace osu.Game.Rulesets.Osu.Objects
Position = Position + Curve.PositionAt(repeat % 2),
StackHeight = StackHeight,
Scale = Scale,
ComboColour = ComboColour,
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
});
}
@@ -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 OpenTK;
namespace osu.Game.Rulesets.Osu.Objects
{
public class SliderCircle : HitCircle
{
private readonly Slider slider;
public SliderCircle(Slider slider)
{
this.slider = slider;
}
public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset);
}
}
+1 -1
View File
@@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu
public override SettingsSubsection CreateSettings() => new OsuSettings();
public override int LegacyID => 0;
public override int? LegacyID => 0;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
@@ -68,6 +68,8 @@ namespace osu.Game.Rulesets.Osu.Scoring
score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss);
}
private const double harshness = 0.01;
protected override void OnNewJudgement(Judgement judgement)
{
base.OnNewJudgement(judgement);
@@ -83,15 +85,15 @@ namespace osu.Game.Rulesets.Osu.Scoring
switch (judgement.Result)
{
case HitResult.Great:
Health.Value += (10.2 - hpDrainRate) * 0.02;
Health.Value += (10.2 - hpDrainRate) * harshness;
break;
case HitResult.Good:
Health.Value += (8 - hpDrainRate) * 0.02;
Health.Value += (8 - hpDrainRate) * harshness;
break;
case HitResult.Meh:
Health.Value += (4 - hpDrainRate) * 0.02;
Health.Value += (4 - hpDrainRate) * harshness;
break;
/*case HitResult.SliderTick:
@@ -99,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
break;*/
case HitResult.Miss:
Health.Value -= hpDrainRate * 0.04;
Health.Value -= hpDrainRate * (harshness * 2);
break;
}
}
@@ -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 NUnit.Framework;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseEditor : EditorTestCase
{
public TestCaseEditor()
: base(new OsuRuleset())
{
}
}
}
@@ -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;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class TestCaseGameplayCursor : OsuTestCase, IProvideCursor
{
private GameplayCursor cursor;
public override IReadOnlyList<Type> RequiredTypes => new [] { typeof(CursorTrail) };
public CursorContainer Cursor => cursor;
public bool ProvidingUserCursor => true;
[BackgroundDependencyLoader]
private void load()
{
Add(cursor = new GameplayCursor { RelativeSizeAxes = Axes.Both });
}
}
}
@@ -10,7 +10,6 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using System.Collections.Generic;
using System;
@@ -61,7 +60,6 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000 + timeOffset,
Position = positionOffset.Value,
ComboColour = Color4.LightSeaGreen
};
circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });

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