1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-09 17:24:04 +08:00

Compare commits

...

293 Commits

239 changed files with 4642 additions and 2397 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.
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
{
if (beatmap.ComboColors.Count == 0)
if (beatmap.ComboColours.Count == 0)
return;
int index = 0;
@@ -31,11 +31,11 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
if (obj.NewCombo)
{
if (lastObj != null) lastObj.LastInCombo = true;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
}
obj.IndexInBeatmap = index++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
obj.ComboColour = beatmap.ComboColours[colourIndex];
lastObj = obj;
}
+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;
}
}
@@ -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));
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
[BackgroundDependencyLoader]
private void load()
{
Child = new Pulp
InternalChild = new Pulp
{
AccentColour = AccentColour,
Size = Size
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
[BackgroundDependencyLoader]
private void load()
{
Children = new[]
InternalChildren = new[]
{
createPulp(HitObject.VisualRepresentation),
border = new Circle
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
if (HitObject.HyperDash)
{
Add(new Pulp
AddInternal(new Pulp
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.Centre,
@@ -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));
@@ -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 });
@@ -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)
@@ -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,
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
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
@@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
});
};
foreach (var tick in HitObject.NestedHitObjects.OfType<HoldNoteTick>())
{
@@ -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
{
@@ -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
{
@@ -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" />
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{
applyStacking(beatmap);
if (beatmap.ComboColors.Count == 0)
if (beatmap.ComboColours.Count == 0)
return;
int comboIndex = 0;
@@ -25,11 +25,11 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
if (obj.NewCombo)
{
comboIndex = 0;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
}
obj.IndexInCurrentCombo = comboIndex++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
obj.ComboColour = beatmap.ComboColours[colourIndex];
}
}
@@ -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;
}
}
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Position = HitObject.StackedPosition;
Scale = new Vector2(h.Scale);
Children = new Drawable[]
InternalChildren = new Drawable[]
{
glow = new GlowPiece
{
@@ -66,6 +66,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
//may not be so correct
Size = circle.DrawSize;
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
@@ -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,7 +12,6 @@ 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;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Container<DrawableSliderTick> ticks;
Container<DrawableRepeatPoint> repeatPoints;
Children = new Drawable[]
InternalChildren = new Drawable[]
{
Body = new SliderBody(s)
{
@@ -55,8 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
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 +83,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
components.Add(drawableRepeatPoint);
AddNested(drawableRepeatPoint);
}
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
}
[BackgroundDependencyLoader]
@@ -103,10 +104,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,
}, false);
}
}
}
@@ -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
}
};
}, false);
}
}
}
+22 -2
View File
@@ -1,23 +1,41 @@
// 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;
using osu.Game.Rulesets.Edit.Types;
namespace osu.Game.Rulesets.Osu.Objects
{
public abstract class OsuHitObject : HitObject, IHasCombo, IHasPosition
public abstract class OsuHitObject : HitObject, IHasCombo, IHasEditablePosition
{
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;
@@ -48,5 +66,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;
}
}
+2 -2
View File
@@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createSliderEnds()
{
HeadCircle = new HitCircle
HeadCircle = new SliderCircle(this)
{
StartTime = StartTime,
Position = Position,
@@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Objects
SampleControlPoint = SampleControlPoint
};
TailCircle = new HitCircle
TailCircle = new SliderCircle(this)
{
StartTime = EndTime,
Position = EndPosition,
@@ -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 });
}
}
}
+42 -14
View File
@@ -3,9 +3,9 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.OpenGL.Buffers;
using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Graphics.Primitives;
@@ -14,11 +14,12 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Timing;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.UI.Cursor
{
internal class CursorTrail : Drawable
internal class CursorTrail : Drawable, IRequireHighFrequencyMousePosition
{
private int currentIndex;
@@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private float time;
public override bool IsPresent => true;
private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData();
private const int max_sprites = 2048;
@@ -96,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
const int fade_clock_reset_threshold = 1000000;
time = (float)(Time.Current - timeOffset) / 500f;
time = (float)(Time.Current - timeOffset) / 300f;
if (time > fade_clock_reset_threshold)
resetTime();
}
@@ -115,14 +118,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override bool OnMouseMove(InputState state)
{
Vector2 pos = state.Mouse.NativeState.Position;
if (lastPosition == null)
{
lastPosition = state.Mouse.NativeState.Position;
lastPosition = pos;
resampler.AddPosition(lastPosition.Value);
return base.OnMouseMove(state);
}
foreach (Vector2 pos2 in resampler.AddPosition(state.Mouse.NativeState.Position))
foreach (Vector2 pos2 in resampler.AddPosition(pos))
{
Trace.Assert(lastPosition.HasValue);
@@ -162,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private class TrailDrawNodeSharedData
{
public VertexBuffer<TexturedVertex2D> VertexBuffer;
public VertexBuffer<TexturedTrailVertex> VertexBuffer;
}
private class TrailDrawNode : DrawNode
@@ -188,7 +193,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
public override void Draw(Action<TexturedVertex2D> vertexAction)
{
if (Shared.VertexBuffer == null)
Shared.VertexBuffer = new QuadVertexBuffer<TexturedVertex2D>(max_sprites, BufferUsageHint.DynamicDraw);
Shared.VertexBuffer = new QuadVertexBuffer<TexturedTrailVertex>(max_sprites, BufferUsageHint.DynamicDraw);
Shader.GetUniform<float>("g_FadeClock").Value = Time;
@@ -205,17 +210,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
int end = start;
Vector2 pos = Parts[i].Position;
ColourInfo colour = DrawInfo.Colour;
colour.TopLeft.Linear.A = Parts[i].Time + colour.TopLeft.Linear.A;
colour.TopRight.Linear.A = Parts[i].Time + colour.TopRight.Linear.A;
colour.BottomLeft.Linear.A = Parts[i].Time + colour.BottomLeft.Linear.A;
colour.BottomRight.Linear.A = Parts[i].Time + colour.BottomRight.Linear.A;
float time = Parts[i].Time;
Texture.DrawQuad(
new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y),
colour,
DrawInfo.Colour,
null,
v => Shared.VertexBuffer.Vertices[end++] = v);
v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex
{
Position = v.Position,
TexturePosition = v.TexturePosition,
Time = time + 1,
Colour = v.Colour,
});
Parts[i].WasUpdated = false;
}
@@ -240,5 +247,26 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
Shader.Unbind();
}
}
[StructLayout(LayoutKind.Sequential)]
public struct TexturedTrailVertex : IEquatable<TexturedTrailVertex>, IVertex
{
[VertexMember(2, VertexAttribPointerType.Float)]
public Vector2 Position;
[VertexMember(4, VertexAttribPointerType.Float)]
public Color4 Colour;
[VertexMember(2, VertexAttribPointerType.Float)]
public Vector2 TexturePosition;
[VertexMember(1, VertexAttribPointerType.Float)]
public float Time;
public bool Equals(TexturedTrailVertex other)
{
return Position.Equals(other.Position)
&& TexturePosition.Equals(other.TexturePosition)
&& Colour.Equals(other.Colour)
&& Time.Equals(other.Time);
}
}
}
}
@@ -20,13 +20,66 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
{
protected override Drawable CreateCursor() => new OsuCursor();
protected override Container<Drawable> Content => fadeContainer;
private readonly Container<Drawable> fadeContainer;
public GameplayCursor()
{
Add(new CursorTrail { Depth = 1 });
InternalChild = fadeContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new CursorTrail { Depth = 1 }
}
};
}
private int downCount;
public bool OnPressed(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
break;
}
return false;
}
public bool OnReleased(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
break;
}
return false;
}
public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
protected override void PopIn()
{
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
}
protected override void PopOut()
{
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
}
public class OsuCursor : Container
{
private Container cursorContainer;
@@ -131,45 +184,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
cursorContainer.Scale = new Vector2(scale);
}
}
public bool OnPressed(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
ActiveCursor.ScaleTo(1).ScaleTo(1.2f, 100, Easing.OutQuad);
break;
}
return false;
}
public bool OnReleased(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
ActiveCursor.ScaleTo(1, 200, Easing.OutQuad);
break;
}
return false;
}
protected override void PopIn()
{
ActiveCursor.FadeTo(1, 250, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
}
protected override void PopOut()
{
ActiveCursor.FadeTo(0, 250, Easing.OutQuint);
ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
}
}
}
+4 -7
View File
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.UI
public class OsuPlayfield : Playfield
{
private readonly Container approachCircles;
private readonly Container judgementLayer;
private readonly JudgementContainer<DrawableOsuJudgement> judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
// Todo: This should not be a thing, but is currently required for the editor
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.UI
RelativeSizeAxes = Axes.Both,
Depth = 2,
},
judgementLayer = new Container
judgementLayer = new JudgementContainer<DrawableOsuJudgement>
{
RelativeSizeAxes = Axes.Both,
Depth = 1,
@@ -75,16 +75,13 @@ namespace osu.Game.Rulesets.Osu.UI
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
var osuJudgement = (OsuJudgement)judgement;
var osuObject = (OsuHitObject)judgedObject.HitObject;
if (!judgedObject.DisplayJudgement)
return;
DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement)
DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject)
{
Origin = Anchor.Centre,
Position = osuObject.StackedEndPosition + osuJudgement.PositionOffset
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + ((OsuJudgement)judgement).PositionOffset
};
judgementLayer.Add(explosion);
@@ -64,10 +64,9 @@
<ItemGroup>
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
<Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
<Compile Include="Edit\Layers\Selection\OsuHitObjectOverlayLayer.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleOverlay.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleOverlay.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\SliderOverlay.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\HitCircleMask.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\SliderCircleMask.cs" />
<Compile Include="Edit\Layers\Selection\Overlays\SliderMask.cs" />
<Compile Include="Edit\OsuEditPlayfield.cs" />
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
<Compile Include="Edit\OsuHitObjectComposer.cs" />
@@ -87,6 +86,7 @@
<Compile Include="Mods\OsuModSpunOut.cs" />
<Compile Include="Mods\OsuModSuddenDeath.cs" />
<Compile Include="Mods\OsuModTarget.cs" />
<Compile Include="Objects\Drawables\DrawableSliderHead.cs" />
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
<Compile Include="Objects\Drawables\Connections\ConnectionRenderer.cs" />
<Compile Include="Objects\Drawables\Connections\FollowPointRenderer.cs" />
@@ -117,6 +117,7 @@
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
<Compile Include="Objects\ISliderProgress.cs" />
<Compile Include="Objects\RepeatPoint.cs" />
<Compile Include="Objects\SliderCircle.cs" />
<Compile Include="Objects\SliderTick.cs" />
<Compile Include="OsuDifficulty\OsuDifficultyCalculator.cs" />
<Compile Include="OsuDifficulty\Preprocessing\OsuDifficultyBeatmap.cs" />
@@ -129,6 +130,8 @@
<Compile Include="Replays\OsuReplayFrame.cs" />
<Compile Include="Replays\OsuReplayInputHandler.cs" />
<Compile Include="Tests\OsuBeatmapConversionTest.cs" />
<Compile Include="Tests\TestCaseEditor.cs" />
<Compile Include="Tests\TestCaseGameplayCursor.cs" />
<Compile Include="Tests\TestCaseHitCircle.cs" />
<Compile Include="Tests\TestCaseHitCircleHidden.cs" />
<Compile Include="Tests\TestCasePerformancePoints.cs" />
@@ -101,16 +101,16 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// The duration of the taiko hit object
double taikoDuration = distance / taikoVelocity;
// For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
// only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
speedAdjustedBeatLength *= speedAdjustment;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
// osu-stable always uses the speed-adjusted beatlength to determine the velocities, but
// only uses it for tick rate if beatmap version < 8
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
speedAdjustedBeatLength *= speedAdjustment;
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
public override double ScoreMultiplier => 0.3;
}
}
@@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModEasy : ModEasy
{
public override string Description => @"Beats move slower, less accuracy required, and three lives!";
}
}
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
public override double ScoreMultiplier => 0.3;
}
}
@@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Taiko.Mods
public class TaikoModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
}
}
@@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModHidden : ModHidden
{
public override string Description => @"The notes fade out before you hit them!";
public override string Description => @"Beats fade out before you hit them!";
public override double ScoreMultiplier => 1.06;
}
}
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModRelax : ModRelax
{
public override string Description => @"Relax! You will no longer get dizzyfied by ninja-like spinners, demanding drumrolls or unexpected katu's.";
public override string Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's.";
}
}
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
RelativeSizeAxes = Axes.Y;
Width = tracker_width;
Children = new[]
InternalChildren = new[]
{
Tracker = new Box
{
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableBarLineMajor(BarLine barLine)
: base(barLine)
{
Add(triangleContainer = new Container
AddInternal(triangleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (state)
{
case ArmedState.Hit:
Content.ScaleTo(0, 100, Easing.OutQuint).Expire();
this.ScaleTo(0, 100, Easing.OutQuint).Expire();
break;
}
}
@@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
const float gravity_time = 300;
const float gravity_travel_height = 200;
Content.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
this.ScaleTo(0.8f, gravity_time * 2, Easing.OutQuad);
this.MoveToY(-gravity_travel_height, gravity_time, Easing.Out)
.Then()

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