mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 06:52:55 +08:00
Merge remote-tracking branch 'FreezyLemon/master' into more-skinning
This commit is contained in:
commit
107ad8cfff
@ -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.
|
@ -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);
|
||||
}
|
||||
|
@ -91,6 +91,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
},
|
||||
new ManiaModRandom(),
|
||||
new ManiaModDualStages(),
|
||||
new ManiaModMirror(),
|
||||
new MultiMod
|
||||
{
|
||||
Mods = new Mod[]
|
||||
@ -112,7 +113,7 @@ 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;
|
||||
|
||||
|
28
osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs
Normal file
28
osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
113
osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs
Normal file
113
osu.Game.Rulesets.Mania/Objects/ManiaHitObjectDifficulty.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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" />
|
||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
@ -14,12 +15,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
public class SliderCircleOverlay : HitObjectOverlay
|
||||
{
|
||||
public SliderCircleOverlay(DrawableHitCircle sliderHead, DrawableSlider slider)
|
||||
: this(sliderHead, sliderHead.Position, slider)
|
||||
: this(sliderHead, Vector2.Zero, slider)
|
||||
{
|
||||
}
|
||||
|
||||
public SliderCircleOverlay(DrawableSliderTail sliderTail, DrawableSlider slider)
|
||||
: this(sliderTail, sliderTail.Position, slider)
|
||||
: this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var beatmap = decoder.Decode(stream);
|
||||
var beatmapInfo = beatmap.BeatmapInfo;
|
||||
var metadata = beatmap.Metadata;
|
||||
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo;
|
||||
var beatmapInfo = decoder.Decode(stream).BeatmapInfo;
|
||||
|
||||
int[] expectedBookmarks =
|
||||
{
|
||||
@ -72,7 +72,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var beatmap = decoder.Decode(stream);
|
||||
var beatmapInfo = beatmap.BeatmapInfo;
|
||||
var metadata = beatmap.Metadata;
|
||||
|
||||
@ -96,7 +96,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var difficulty = decoder.DecodeBeatmap(stream).BeatmapInfo.BaseDifficulty;
|
||||
var difficulty = decoder.Decode(stream).BeatmapInfo.BaseDifficulty;
|
||||
|
||||
Assert.AreEqual(6.5f, difficulty.DrainRate);
|
||||
Assert.AreEqual(4, difficulty.CircleSize);
|
||||
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var beatmap = decoder.Decode(stream);
|
||||
var metadata = beatmap.Metadata;
|
||||
var breakPoint = beatmap.Breaks[0];
|
||||
|
||||
@ -132,7 +132,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var beatmap = decoder.Decode(stream);
|
||||
var controlPoints = beatmap.ControlPointInfo;
|
||||
|
||||
Assert.AreEqual(4, controlPoints.TimingPoints.Count);
|
||||
@ -167,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var comboColors = decoder.DecodeBeatmap(stream).ComboColors;
|
||||
var comboColors = decoder.Decode(stream).ComboColors;
|
||||
|
||||
Color4[] expectedColors =
|
||||
{
|
||||
@ -191,7 +191,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var hitObjects = decoder.DecodeBeatmap(stream).HitObjects;
|
||||
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||
|
||||
var curveData = hitObjects[0] as IHasCurve;
|
||||
var positionData = hitObjects[0] as IHasPosition;
|
||||
|
@ -18,11 +18,11 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
[Test]
|
||||
public void TestDecodeStoryboardEvents()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
var decoder = new LegacyStoryboardDecoder();
|
||||
using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
|
||||
var storyboard = decoder.Decode(stream);
|
||||
|
||||
Assert.IsTrue(storyboard.HasDrawable);
|
||||
Assert.AreEqual(4, storyboard.Layers.Count());
|
||||
|
@ -159,7 +159,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var sr = new StreamReader(stream))
|
||||
{
|
||||
|
||||
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.DecodeBeatmap(sr);
|
||||
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
|
||||
using (var ms = new MemoryStream())
|
||||
using (var sw = new StreamWriter(ms))
|
||||
using (var sr2 = new StreamReader(ms))
|
||||
@ -168,7 +168,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
sw.Flush();
|
||||
|
||||
ms.Position = 0;
|
||||
return (legacyDecoded, new JsonBeatmapDecoder().DecodeBeatmap(sr2));
|
||||
return (legacyDecoded, new JsonBeatmapDecoder().Decode(sr2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
|
||||
BeatmapMetadata meta;
|
||||
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||
meta = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
|
||||
meta = Decoder.GetDecoder<Beatmap>(stream).Decode(stream).Metadata;
|
||||
|
||||
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
||||
Assert.AreEqual("Soleily", meta.Artist);
|
||||
|
@ -58,6 +58,12 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
checkSupporterTag(false);
|
||||
|
||||
AddStep("Show null dummy", () => profile.ShowUser(new User
|
||||
{
|
||||
Username = @"Null",
|
||||
Id = 1,
|
||||
}, false));
|
||||
|
||||
AddStep("Show ppy", () => profile.ShowUser(new User
|
||||
{
|
||||
Username = @"peppy",
|
||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Beatmaps
|
||||
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
||||
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||
|
||||
public List<Color4> ComboColors = new List<Color4>
|
||||
{
|
||||
new Color4(17, 136, 170, 255),
|
||||
@ -85,9 +86,13 @@ namespace osu.Game.Beatmaps
|
||||
/// Constructs a new beatmap.
|
||||
/// </summary>
|
||||
/// <param name="original">The original beatmap to use the parameters of.</param>
|
||||
public Beatmap(Beatmap original = null)
|
||||
public Beatmap(Beatmap original)
|
||||
: base(original)
|
||||
{
|
||||
}
|
||||
|
||||
public Beatmap()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
BeatmapMetadata metadata;
|
||||
using (var stream = new StreamReader(reader.GetStream(mapName)))
|
||||
metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
|
||||
metadata = Decoder.GetDecoder<Beatmap>(stream).Decode(stream).Metadata;
|
||||
|
||||
return new BeatmapSetInfo
|
||||
{
|
||||
@ -328,8 +328,8 @@ namespace osu.Game.Beatmaps
|
||||
raw.CopyTo(ms);
|
||||
ms.Position = 0;
|
||||
|
||||
var decoder = Decoder.GetDecoder(sr);
|
||||
Beatmap beatmap = decoder.DecodeBeatmap(sr);
|
||||
var decoder = Decoder.GetDecoder<Beatmap>(sr);
|
||||
Beatmap beatmap = decoder.Decode(sr);
|
||||
|
||||
beatmap.BeatmapInfo.Path = name;
|
||||
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Graphics.Textures;
|
||||
using osu.Game.Storyboards;
|
||||
@ -30,10 +31,7 @@ namespace osu.Game.Beatmaps
|
||||
try
|
||||
{
|
||||
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
||||
{
|
||||
Decoder decoder = Decoder.GetDecoder(stream);
|
||||
return decoder.DecodeBeatmap(stream);
|
||||
}
|
||||
return Decoder.GetDecoder<Beatmap>(stream).Decode(stream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -78,23 +76,23 @@ namespace osu.Game.Beatmaps
|
||||
Storyboard storyboard;
|
||||
try
|
||||
{
|
||||
using (var beatmap = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
||||
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
|
||||
{
|
||||
Decoder decoder = Decoder.GetDecoder(beatmap);
|
||||
var decoder = Decoder.GetDecoder<Storyboard>(stream);
|
||||
|
||||
// todo: support loading from both set-wide storyboard *and* beatmap specific.
|
||||
|
||||
if (BeatmapSetInfo?.StoryboardFile == null)
|
||||
storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap);
|
||||
storyboard = decoder.Decode(stream);
|
||||
else
|
||||
{
|
||||
using (var reader = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
|
||||
storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(beatmap, reader);
|
||||
using (var secondaryStream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile))))
|
||||
storyboard = decoder.Decode(stream, secondaryStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, "Storyboard failed to load");
|
||||
storyboard = new Storyboard();
|
||||
}
|
||||
|
||||
|
@ -24,9 +24,15 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null)
|
||||
{
|
||||
Beatmap = CreateBeatmapConverter(beatmap).Convert(beatmap);
|
||||
Mods = mods ?? new Mod[0];
|
||||
|
||||
var converter = CreateBeatmapConverter(beatmap);
|
||||
|
||||
foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<T>>())
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
|
||||
Beatmap = converter.Convert(beatmap);
|
||||
|
||||
ApplyMods(Mods);
|
||||
|
||||
PreprocessHitObjects();
|
||||
|
@ -4,38 +4,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using osu.Game.Storyboards;
|
||||
using System.Linq;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public abstract class Decoder<TOutput> : Decoder
|
||||
where TOutput : new()
|
||||
{
|
||||
protected virtual TOutput CreateTemplateObject() => new TOutput();
|
||||
|
||||
public TOutput Decode(StreamReader primaryStream, params StreamReader[] otherStreams)
|
||||
{
|
||||
var output = CreateTemplateObject();
|
||||
foreach (StreamReader stream in new[] { primaryStream }.Concat(otherStreams))
|
||||
ParseStreamInto(stream, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
protected abstract void ParseStreamInto(StreamReader stream, TOutput beatmap);
|
||||
}
|
||||
|
||||
public abstract class Decoder
|
||||
{
|
||||
private static readonly Dictionary<string, Func<string, Decoder>> decoders = new Dictionary<string, Func<string, Decoder>>();
|
||||
private static readonly Dictionary<Type, Dictionary<string, Func<string, Decoder>>> decoders = new Dictionary<Type, Dictionary<string, Func<string, Decoder>>>();
|
||||
|
||||
static Decoder()
|
||||
{
|
||||
LegacyDecoder.Register();
|
||||
LegacyBeatmapDecoder.Register();
|
||||
JsonBeatmapDecoder.Register();
|
||||
LegacyStoryboardDecoder.Register();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Beatmap"/>.
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream pointing to the <see cref="Beatmap"/>.</param>
|
||||
public static Decoder GetDecoder(StreamReader stream)
|
||||
public static Decoder<T> GetDecoder<T>(StreamReader stream)
|
||||
where T : new()
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
|
||||
if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
|
||||
throw new IOException(@"Unknown decoder type");
|
||||
|
||||
string line;
|
||||
do
|
||||
{ line = stream.ReadLine()?.Trim(); }
|
||||
while (line != null && line.Length == 0);
|
||||
{
|
||||
line = stream.ReadLine()?.Trim();
|
||||
} while (line != null && line.Length == 0);
|
||||
|
||||
if (line == null || !decoders.ContainsKey(line))
|
||||
if (line == null)
|
||||
throw new IOException(@"Unknown file format");
|
||||
|
||||
return decoders[line](line);
|
||||
var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault();
|
||||
if (decoder == null)
|
||||
throw new IOException(@"Unknown file format");
|
||||
|
||||
return (Decoder<T>)decoder.Invoke(line);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -43,41 +69,12 @@ namespace osu.Game.Beatmaps.Formats
|
||||
/// </summary>
|
||||
/// <param name="magic">A string in the file which triggers this decoder to be used.</param>
|
||||
/// <param name="constructor">A function which constructs the <see cref="Decoder"/> given <paramref name="magic"/>.</param>
|
||||
protected static void AddDecoder(string magic, Func<string, Decoder> constructor)
|
||||
protected static void AddDecoder<T>(string magic, Func<string, Decoder> constructor)
|
||||
{
|
||||
decoders[magic] = constructor;
|
||||
if (!decoders.TryGetValue(typeof(T), out var typedDecoders))
|
||||
decoders.Add(typeof(T), typedDecoders = new Dictionary<string, Func<string, Decoder>>());
|
||||
|
||||
typedDecoders[magic] = constructor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="Decoder"/> to parse a <see cref="Storyboard"/>
|
||||
/// </summary>
|
||||
public abstract Decoder GetStoryboardDecoder();
|
||||
|
||||
public virtual Beatmap DecodeBeatmap(StreamReader stream)
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata(),
|
||||
BaseDifficulty = new BeatmapDifficulty(),
|
||||
},
|
||||
};
|
||||
|
||||
ParseBeatmap(stream, beatmap);
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap);
|
||||
|
||||
public virtual Storyboard DecodeStoryboard(params StreamReader[] streams)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
foreach (StreamReader stream in streams)
|
||||
ParseStoryboard(stream, storyboard);
|
||||
return storyboard;
|
||||
}
|
||||
|
||||
protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard);
|
||||
}
|
||||
}
|
||||
|
@ -3,20 +3,17 @@
|
||||
|
||||
using System.IO;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Storyboards;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public class JsonBeatmapDecoder : Decoder
|
||||
public class JsonBeatmapDecoder : Decoder<Beatmap>
|
||||
{
|
||||
public static void Register()
|
||||
{
|
||||
AddDecoder("{", m => new JsonBeatmapDecoder());
|
||||
AddDecoder<Beatmap>("{", m => new JsonBeatmapDecoder());
|
||||
}
|
||||
|
||||
public override Decoder GetStoryboardDecoder() => this;
|
||||
|
||||
protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
|
||||
protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap)
|
||||
{
|
||||
stream.BaseStream.Position = 0;
|
||||
stream.DiscardBufferedData();
|
||||
@ -26,10 +23,5 @@ namespace osu.Game.Beatmaps.Formats
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
|
||||
protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
|
||||
{
|
||||
// throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
@ -12,8 +13,10 @@ using osu.Framework;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public class LegacyBeatmapDecoder : LegacyDecoder
|
||||
public class LegacyBeatmapDecoder : LegacyDecoder<Beatmap>
|
||||
{
|
||||
public const int LATEST_VERSION = 14;
|
||||
|
||||
private Beatmap beatmap;
|
||||
|
||||
private bool hasCustomColours;
|
||||
@ -22,6 +25,11 @@ namespace osu.Game.Beatmaps.Formats
|
||||
private LegacySampleBank defaultSampleBank;
|
||||
private int defaultSampleVolume = 100;
|
||||
|
||||
public static void Register()
|
||||
{
|
||||
AddDecoder<Beatmap>(@"osu file format v", m => new LegacyBeatmapDecoder(int.Parse(m.Split('v').Last())));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// lazer's audio timings in general doesn't match stable. this is the result of user testing, albeit limited.
|
||||
/// This only seems to be required on windows. We need to eventually figure out why, with a bit of luck.
|
||||
@ -35,29 +43,18 @@ namespace osu.Game.Beatmaps.Formats
|
||||
|
||||
private readonly int offset = UniversalOffset;
|
||||
|
||||
public LegacyBeatmapDecoder()
|
||||
public LegacyBeatmapDecoder(int version = LATEST_VERSION) : base(version)
|
||||
{
|
||||
}
|
||||
|
||||
public LegacyBeatmapDecoder(string header)
|
||||
{
|
||||
BeatmapVersion = int.Parse(header.Substring(17));
|
||||
|
||||
// BeatmapVersion 4 and lower had an incorrect offset (stable has this set as 24ms off)
|
||||
offset += BeatmapVersion < 5 ? 24 : 0;
|
||||
offset += FormatVersion < 5 ? 24 : 0;
|
||||
}
|
||||
|
||||
protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
|
||||
protected override void ParseStreamInto(StreamReader stream, Beatmap beatmap)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
if (beatmap == null)
|
||||
throw new ArgumentNullException(nameof(beatmap));
|
||||
|
||||
this.beatmap = beatmap;
|
||||
this.beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion;
|
||||
this.beatmap.BeatmapInfo.BeatmapVersion = FormatVersion;
|
||||
|
||||
ParseContent(stream);
|
||||
base.ParseStreamInto(stream, beatmap);
|
||||
|
||||
// objects may be out of order *only* if a user has manually edited an .osu file.
|
||||
// unfortunately there are ranked maps in this state (example: https://osu.ppy.sh/s/594828).
|
||||
@ -67,14 +64,9 @@ namespace osu.Game.Beatmaps.Formats
|
||||
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
|
||||
}
|
||||
|
||||
protected override bool ShouldSkipLine(string line)
|
||||
{
|
||||
if (base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_");
|
||||
|
||||
protected override void ProcessSection(Section section, string line)
|
||||
protected override void ParseLine(Beatmap beatmap, Section section, string line)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
|
@ -4,47 +4,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Storyboards;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public abstract class LegacyDecoder : Decoder
|
||||
public abstract class LegacyDecoder<T> : Decoder<T>
|
||||
where T : new()
|
||||
{
|
||||
public static void Register()
|
||||
protected readonly int FormatVersion;
|
||||
|
||||
protected LegacyDecoder(int version)
|
||||
{
|
||||
AddDecoder(@"osu file format v14", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v13", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v12", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v11", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v10", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v9", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v8", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v7", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v6", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v5", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v4", m => new LegacyBeatmapDecoder(m));
|
||||
AddDecoder(@"osu file format v3", m => new LegacyBeatmapDecoder(m));
|
||||
// TODO: differences between versions
|
||||
FormatVersion = version;
|
||||
}
|
||||
|
||||
protected int BeatmapVersion;
|
||||
|
||||
public override Decoder GetStoryboardDecoder() => new LegacyStoryboardDecoder(BeatmapVersion);
|
||||
|
||||
public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream));
|
||||
|
||||
protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected void ParseContent(StreamReader stream)
|
||||
protected override void ParseStreamInto(StreamReader stream, T beatmap)
|
||||
{
|
||||
Section section = Section.None;
|
||||
|
||||
@ -54,13 +27,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
if (ShouldSkipLine(line))
|
||||
continue;
|
||||
|
||||
// It's already set in ParseBeatmap... why do it again?
|
||||
//if (line.StartsWith(@"osu file format v"))
|
||||
//{
|
||||
// Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
|
||||
// continue;
|
||||
//}
|
||||
|
||||
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
||||
{
|
||||
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
|
||||
@ -68,18 +34,13 @@ namespace osu.Game.Beatmaps.Formats
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcessSection(section, line);
|
||||
ParseLine(beatmap, section, line);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool ShouldSkipLine(string line)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.StartsWith("//");
|
||||
|
||||
protected abstract void ProcessSection(Section section, string line);
|
||||
protected abstract void ParseLine(T output, Section section, string line);
|
||||
|
||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using OpenTK;
|
||||
@ -13,37 +13,34 @@ using osu.Game.Storyboards;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
public class LegacyStoryboardDecoder : LegacyDecoder
|
||||
public class LegacyStoryboardDecoder : LegacyDecoder<Storyboard>
|
||||
{
|
||||
private Storyboard storyboard;
|
||||
|
||||
private StoryboardSprite storyboardSprite;
|
||||
private CommandTimelineGroup timelineGroup;
|
||||
|
||||
private Storyboard storyboard;
|
||||
|
||||
private readonly Dictionary<string, string> variables = new Dictionary<string, string>();
|
||||
|
||||
public LegacyStoryboardDecoder()
|
||||
: base(0)
|
||||
{
|
||||
}
|
||||
|
||||
public LegacyStoryboardDecoder(int beatmapVersion)
|
||||
public static void Register()
|
||||
{
|
||||
BeatmapVersion = beatmapVersion;
|
||||
// note that this isn't completely correct
|
||||
AddDecoder<Storyboard>(@"osu file format v", m => new LegacyStoryboardDecoder());
|
||||
AddDecoder<Storyboard>(@"[Events]", m => new LegacyStoryboardDecoder());
|
||||
}
|
||||
|
||||
protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard)
|
||||
protected override void ParseStreamInto(StreamReader stream, Storyboard storyboard)
|
||||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
if (storyboard == null)
|
||||
throw new ArgumentNullException(nameof(storyboard));
|
||||
|
||||
this.storyboard = storyboard;
|
||||
|
||||
ParseContent(stream);
|
||||
base.ParseStreamInto(stream, storyboard);
|
||||
}
|
||||
|
||||
protected override void ProcessSection(Section section, string line)
|
||||
protected override void ParseLine(Storyboard storyboard, Section section, string line)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
@ -80,38 +77,38 @@ namespace osu.Game.Beatmaps.Formats
|
||||
switch (type)
|
||||
{
|
||||
case EventType.Sprite:
|
||||
{
|
||||
var layer = parseLayer(split[1]);
|
||||
var origin = parseOrigin(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||
storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
|
||||
storyboard.GetLayer(layer).Add(storyboardSprite);
|
||||
}
|
||||
{
|
||||
var layer = parseLayer(split[1]);
|
||||
var origin = parseOrigin(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||
storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
|
||||
storyboard.GetLayer(layer).Add(storyboardSprite);
|
||||
}
|
||||
break;
|
||||
case EventType.Animation:
|
||||
{
|
||||
var layer = parseLayer(split[1]);
|
||||
var origin = parseOrigin(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||
var frameCount = int.Parse(split[6]);
|
||||
var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo);
|
||||
var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever;
|
||||
storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType);
|
||||
storyboard.GetLayer(layer).Add(storyboardSprite);
|
||||
}
|
||||
{
|
||||
var layer = parseLayer(split[1]);
|
||||
var origin = parseOrigin(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
|
||||
var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
|
||||
var frameCount = int.Parse(split[6]);
|
||||
var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo);
|
||||
var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever;
|
||||
storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType);
|
||||
storyboard.GetLayer(layer).Add(storyboardSprite);
|
||||
}
|
||||
break;
|
||||
case EventType.Sample:
|
||||
{
|
||||
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||
var layer = parseLayer(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
|
||||
storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume));
|
||||
}
|
||||
{
|
||||
var time = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||
var layer = parseLayer(split[2]);
|
||||
var path = cleanFilename(split[3]);
|
||||
var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
|
||||
storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -124,120 +121,120 @@ namespace osu.Game.Beatmaps.Formats
|
||||
switch (commandType)
|
||||
{
|
||||
case "T":
|
||||
{
|
||||
var triggerName = split[1];
|
||||
var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue;
|
||||
var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue;
|
||||
var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0;
|
||||
timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber);
|
||||
}
|
||||
{
|
||||
var triggerName = split[1];
|
||||
var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue;
|
||||
var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue;
|
||||
var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0;
|
||||
timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber);
|
||||
}
|
||||
break;
|
||||
case "L":
|
||||
{
|
||||
var startTime = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||
var loopCount = int.Parse(split[2]);
|
||||
timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount);
|
||||
}
|
||||
{
|
||||
var startTime = double.Parse(split[1], CultureInfo.InvariantCulture);
|
||||
var loopCount = int.Parse(split[2]);
|
||||
timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (string.IsNullOrEmpty(split[3]))
|
||||
split[3] = split[2];
|
||||
|
||||
var easing = (Easing)int.Parse(split[1]);
|
||||
var startTime = double.Parse(split[2], CultureInfo.InvariantCulture);
|
||||
var endTime = double.Parse(split[3], CultureInfo.InvariantCulture);
|
||||
|
||||
switch (commandType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(split[3]))
|
||||
split[3] = split[2];
|
||||
|
||||
var easing = (Easing)int.Parse(split[1]);
|
||||
var startTime = double.Parse(split[2], CultureInfo.InvariantCulture);
|
||||
var endTime = double.Parse(split[3], CultureInfo.InvariantCulture);
|
||||
|
||||
switch (commandType)
|
||||
case "F":
|
||||
{
|
||||
case "F":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "S":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue));
|
||||
}
|
||||
break;
|
||||
case "V":
|
||||
{
|
||||
var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
||||
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
|
||||
}
|
||||
break;
|
||||
case "R":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue));
|
||||
}
|
||||
break;
|
||||
case "M":
|
||||
{
|
||||
var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
||||
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
||||
timelineGroup?.X.Add(easing, startTime, endTime, startX, endX);
|
||||
timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY);
|
||||
}
|
||||
break;
|
||||
case "MX":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "MY":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "C":
|
||||
{
|
||||
var startRed = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture);
|
||||
var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed;
|
||||
var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen;
|
||||
var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue;
|
||||
timelineGroup?.Colour.Add(easing, startTime, endTime,
|
||||
new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1),
|
||||
new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1));
|
||||
}
|
||||
break;
|
||||
case "P":
|
||||
{
|
||||
var type = split[4];
|
||||
switch (type)
|
||||
{
|
||||
case "A":
|
||||
timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit);
|
||||
break;
|
||||
case "H":
|
||||
timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime);
|
||||
break;
|
||||
case "V":
|
||||
timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDataException($@"Unknown command type: {commandType}");
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "S":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue));
|
||||
}
|
||||
break;
|
||||
case "V":
|
||||
{
|
||||
var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
||||
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
||||
timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
|
||||
}
|
||||
break;
|
||||
case "R":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue));
|
||||
}
|
||||
break;
|
||||
case "M":
|
||||
{
|
||||
var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
|
||||
var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
|
||||
timelineGroup?.X.Add(easing, startTime, endTime, startX, endX);
|
||||
timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY);
|
||||
}
|
||||
break;
|
||||
case "MX":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "MY":
|
||||
{
|
||||
var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
|
||||
timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue);
|
||||
}
|
||||
break;
|
||||
case "C":
|
||||
{
|
||||
var startRed = float.Parse(split[4], CultureInfo.InvariantCulture);
|
||||
var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture);
|
||||
var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture);
|
||||
var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed;
|
||||
var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen;
|
||||
var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue;
|
||||
timelineGroup?.Colour.Add(easing, startTime, endTime,
|
||||
new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1),
|
||||
new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1));
|
||||
}
|
||||
break;
|
||||
case "P":
|
||||
{
|
||||
var type = split[4];
|
||||
switch (type)
|
||||
{
|
||||
case "A":
|
||||
timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit);
|
||||
break;
|
||||
case "H":
|
||||
timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime);
|
||||
break;
|
||||
case "V":
|
||||
timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InvalidDataException($@"Unknown command type: {commandType}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -269,6 +266,7 @@ namespace osu.Game.Beatmaps.Formats
|
||||
case LegacyOrigins.BottomRight:
|
||||
return Anchor.BottomRight;
|
||||
}
|
||||
|
||||
throw new InvalidDataException($@"Unknown origin: {value}");
|
||||
}
|
||||
|
||||
|
@ -66,8 +66,10 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount;
|
||||
|
||||
content.Position = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000), content.Position, offset, 0, 1000, Easing.OutQuint);
|
||||
content.Scale = new Vector2(1 + ParallaxAmount);
|
||||
double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000);
|
||||
|
||||
content.Position = Interpolation.ValueAt(elapsed, content.Position, offset, 0, 1000, Easing.OutQuint);
|
||||
content.Scale = Interpolation.ValueAt(elapsed, content.Scale, new Vector2(1 + ParallaxAmount), 0, 1000, Easing.OutQuint);
|
||||
}
|
||||
|
||||
firstUpdate = false;
|
||||
|
66
osu.Game/Graphics/DrawableDate.cs
Normal file
66
osu.Game/Graphics/DrawableDate.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// 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 Humanizer;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace osu.Game.Graphics
|
||||
{
|
||||
public class DrawableDate : OsuSpriteText, IHasTooltip
|
||||
{
|
||||
private readonly DateTimeOffset date;
|
||||
private ScheduledDelegate updateTask;
|
||||
|
||||
public DrawableDate(DateTimeOffset date)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Font = "Exo2.0-RegularItalic";
|
||||
|
||||
this.date = date.ToLocalTime();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
updateTime();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Scheduler.Add(updateTimeWithReschedule);
|
||||
}
|
||||
|
||||
private void updateTimeWithReschedule()
|
||||
{
|
||||
updateTime();
|
||||
|
||||
var diffToNow = DateTimeOffset.Now.Subtract(date);
|
||||
|
||||
double timeUntilNextUpdate = 1000;
|
||||
if (diffToNow.TotalSeconds > 60)
|
||||
{
|
||||
timeUntilNextUpdate *= 60;
|
||||
if (diffToNow.TotalMinutes > 60)
|
||||
{
|
||||
timeUntilNextUpdate *= 60;
|
||||
|
||||
if (diffToNow.TotalHours > 24)
|
||||
timeUntilNextUpdate *= 24;
|
||||
}
|
||||
}
|
||||
|
||||
Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate);
|
||||
}
|
||||
|
||||
public override bool HandleMouseInput => true;
|
||||
|
||||
private void updateTime() => Text = date.Humanize();
|
||||
public string TooltipText => date.ToString();
|
||||
}
|
||||
}
|
@ -130,11 +130,7 @@ namespace osu.Game.Overlays.Profile
|
||||
}
|
||||
}
|
||||
},
|
||||
infoTextLeft = new OsuTextFlowContainer(t =>
|
||||
{
|
||||
t.TextSize = 14;
|
||||
t.Alpha = 0.8f;
|
||||
})
|
||||
infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14)
|
||||
{
|
||||
X = UserProfileOverlay.CONTENT_X_MARGIN,
|
||||
Y = cover_height + 20,
|
||||
@ -318,11 +314,23 @@ namespace osu.Game.Overlays.Profile
|
||||
colourBar.Show();
|
||||
}
|
||||
|
||||
void boldItalic(SpriteText t)
|
||||
void boldItalic(SpriteText t) => t.Font = @"Exo2.0-BoldItalic";
|
||||
void lightText(SpriteText t) => t.Alpha = 0.8f;
|
||||
|
||||
OsuSpriteText createScoreText(string text) => new OsuSpriteText
|
||||
{
|
||||
t.Font = @"Exo2.0-BoldItalic";
|
||||
t.Alpha = 1;
|
||||
}
|
||||
TextSize = 14,
|
||||
Text = text
|
||||
};
|
||||
|
||||
OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
|
||||
{
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-Bold",
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Text = text
|
||||
};
|
||||
|
||||
if (user.Age != null)
|
||||
{
|
||||
@ -331,7 +339,7 @@ namespace osu.Game.Overlays.Profile
|
||||
|
||||
if (user.Country != null)
|
||||
{
|
||||
infoTextLeft.AddText("from ");
|
||||
infoTextLeft.AddText("from ", lightText);
|
||||
infoTextLeft.AddText(user.Country.FullName, boldItalic);
|
||||
countryFlag.Country = user.Country;
|
||||
}
|
||||
@ -344,18 +352,18 @@ namespace osu.Game.Overlays.Profile
|
||||
}
|
||||
else
|
||||
{
|
||||
infoTextLeft.AddText("Joined ");
|
||||
infoTextLeft.AddText(user.JoinDate.LocalDateTime.ToShortDateString(), boldItalic);
|
||||
infoTextLeft.AddText("Joined ", lightText);
|
||||
infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic);
|
||||
}
|
||||
|
||||
infoTextLeft.NewLine();
|
||||
infoTextLeft.AddText("Last seen ");
|
||||
infoTextLeft.AddText(user.LastVisit.LocalDateTime.ToShortDateString(), boldItalic);
|
||||
infoTextLeft.AddText("Last seen ", lightText);
|
||||
infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic);
|
||||
infoTextLeft.NewParagraph();
|
||||
|
||||
if (user.PlayStyle?.Length > 0)
|
||||
{
|
||||
infoTextLeft.AddText("Plays with ");
|
||||
infoTextLeft.AddText("Plays with ", lightText);
|
||||
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
|
||||
}
|
||||
|
||||
@ -411,23 +419,6 @@ namespace osu.Game.Overlays.Profile
|
||||
}
|
||||
}
|
||||
|
||||
// These could be local functions when C# 7 enabled
|
||||
|
||||
private OsuSpriteText createScoreText(string text) => new OsuSpriteText
|
||||
{
|
||||
TextSize = 14,
|
||||
Text = text
|
||||
};
|
||||
|
||||
private OsuSpriteText createScoreNumberText(string text) => new OsuSpriteText
|
||||
{
|
||||
TextSize = 14,
|
||||
Font = @"Exo2.0-Bold",
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Text = text
|
||||
};
|
||||
|
||||
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str)) return;
|
||||
@ -436,10 +427,12 @@ namespace osu.Game.Overlays.Profile
|
||||
if (url != null)
|
||||
{
|
||||
infoTextRight.AddLink(" " + str, url);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
infoTextRight.AddText(" " + str);
|
||||
}
|
||||
|
||||
infoTextRight.NewLine();
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Profile
|
||||
{
|
||||
placeholder.FadeIn(fade_duration, Easing.Out);
|
||||
|
||||
if (user == null)
|
||||
if (user?.Statistics?.Ranks.Global == null)
|
||||
{
|
||||
rankText.Text = string.Empty;
|
||||
performanceText.Text = string.Empty;
|
||||
@ -105,7 +105,7 @@ namespace osu.Game.Overlays.Profile
|
||||
return;
|
||||
}
|
||||
|
||||
int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global };
|
||||
int[] userRanks = user.RankHistory?.Data ?? new[] { user.Statistics.Ranks.Global.Value };
|
||||
ranks = userRanks.Select((x, index) => new KeyValuePair<int, int>(index, x)).Where(x => x.Value != 0).ToArray();
|
||||
|
||||
if (ranks.Length > 1)
|
||||
|
@ -54,12 +54,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
||||
RightFlowContainer.SetLayoutPosition(text, 1);
|
||||
|
||||
LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap));
|
||||
LeftFlowContainer.Add(new OsuSpriteText
|
||||
{
|
||||
Text = Score.Date.LocalDateTime.ToShortDateString(),
|
||||
TextSize = 11,
|
||||
Colour = OsuColour.Gray(0xAA),
|
||||
});
|
||||
LeftFlowContainer.Add(new DrawableDate(Score.Date));
|
||||
|
||||
foreach (Mod mod in Score.Mods)
|
||||
modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) });
|
||||
|
@ -6,7 +6,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Chat;
|
||||
@ -40,14 +39,12 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||
RelativeSizeAxes = Axes.X,
|
||||
});
|
||||
|
||||
RightFlowContainer.Add(new OsuSpriteText
|
||||
RightFlowContainer.Add(new DrawableDate(activity.CreatedAt)
|
||||
{
|
||||
Text = activity.CreatedAt.LocalDateTime.ToShortDateString(),
|
||||
TextSize = 13,
|
||||
Colour = OsuColour.Gray(0xAA),
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Font = "Exo2.0-RegularItalic",
|
||||
TextSize = 12,
|
||||
Colour = OsuColour.Gray(0xAA),
|
||||
});
|
||||
|
||||
var formatted = createMessage();
|
||||
|
@ -169,15 +169,18 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
Header.User = user;
|
||||
|
||||
foreach (string id in user.ProfileOrder)
|
||||
if (user.ProfileOrder != null)
|
||||
{
|
||||
var sec = sections.FirstOrDefault(s => s.Identifier == id);
|
||||
if (sec != null)
|
||||
foreach (string id in user.ProfileOrder)
|
||||
{
|
||||
sec.User.Value = user;
|
||||
var sec = sections.FirstOrDefault(s => s.Identifier == id);
|
||||
if (sec != null)
|
||||
{
|
||||
sec.User.Value = user;
|
||||
|
||||
sectionsContainer.Add(sec);
|
||||
tabs.AddItem(sec);
|
||||
sectionsContainer.Add(sec);
|
||||
tabs.AddItem(sec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,8 +188,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
string[] split = str.Split(':');
|
||||
|
||||
var bank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
||||
var addbank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
||||
var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
||||
var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
||||
|
||||
// Let's not implement this for now, because this doesn't fit nicely into the bank structure
|
||||
//string sampleFile = split2.Length > 4 ? split2[4] : string.Empty;
|
||||
|
@ -2,7 +2,9 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Scoring
|
||||
@ -23,9 +25,15 @@ namespace osu.Game.Rulesets.Scoring
|
||||
|
||||
protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score)
|
||||
{
|
||||
Beatmap = CreateBeatmapConverter().Convert(beatmap);
|
||||
Score = score;
|
||||
|
||||
var converter = CreateBeatmapConverter();
|
||||
|
||||
foreach (var mod in score.Mods.OfType<IApplicableToBeatmapConverter<TObject>>())
|
||||
mod.ApplyToBeatmapConverter(converter);
|
||||
|
||||
Beatmap = converter.Convert(beatmap);
|
||||
|
||||
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
|
||||
diffCalc.Calculate(attributes);
|
||||
}
|
||||
|
@ -112,9 +112,9 @@ namespace osu.Game.Tests.Beatmaps
|
||||
using (var resStream = openResource($"{resource_namespace}.{name}.osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var decoder = Decoder.GetDecoder(stream);
|
||||
var decoder = Decoder.GetDecoder<Beatmap>(stream);
|
||||
((LegacyBeatmapDecoder)decoder).ApplyOffsets = false;
|
||||
return decoder.DecodeBeatmap(stream);
|
||||
return decoder.Decode(stream);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data)))
|
||||
using (var reader = new StreamReader(stream))
|
||||
beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).DecodeBeatmap(reader);
|
||||
beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
@ -73,10 +73,10 @@ namespace osu.Game.Users
|
||||
public struct UserRanks
|
||||
{
|
||||
[JsonProperty(@"global")]
|
||||
public int Global;
|
||||
public int? Global;
|
||||
|
||||
[JsonProperty(@"country")]
|
||||
public int Country;
|
||||
public int? Country;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -287,6 +287,7 @@
|
||||
<Compile Include="Database\MutableDatabaseBackedStore.cs" />
|
||||
<Compile Include="Database\SingletonContextFactory.cs" />
|
||||
<Compile Include="Graphics\Containers\LinkFlowContainer.cs" />
|
||||
<Compile Include="Graphics\DrawableDate.cs" />
|
||||
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
|
||||
<Compile Include="IO\Archives\ArchiveReader.cs" />
|
||||
<Compile Include="IO\Archives\LegacyFilesystemReader.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user