mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 12:33:01 +08:00
Merge branch 'master' into fix-mod-button-sounds
This commit is contained in:
commit
fd124d2ee6
@ -1 +1 @@
|
|||||||
Subproject commit 41e2a0a4304544fb67779c21cad1435c105982d5
|
Subproject commit d8d4f55e10ac553223db75874bae6ae4894b739a
|
@ -1 +1 @@
|
|||||||
Subproject commit 7bb0782200abadf73b79ed1a3bc1d5b926c6a81e
|
Subproject commit 6e145ed50274539ee827fdc3d1fda1e130b070fd
|
@ -16,11 +16,9 @@
|
|||||||
<language>en-AU</language>
|
<language>en-AU</language>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
|
<file src="**.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||||
<file src="*.dll" target="lib\net45\"/>
|
<file src="**.dll" target="lib\net45\"/>
|
||||||
<file src="*.config" target="lib\net45\"/>
|
<file src="**.config" target="lib\net45\"/>
|
||||||
<file src="x86\*.dll" target="lib\net45\x86\"/>
|
|
||||||
<file src="x64\*.dll" target="lib\net45\x64\"/>
|
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
||||||
|
|
||||||
|
@ -16,29 +16,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
{
|
{
|
||||||
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
||||||
{
|
{
|
||||||
if (beatmap.ComboColours.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
int colourIndex = 0;
|
|
||||||
|
|
||||||
CatchHitObject lastObj = null;
|
|
||||||
|
|
||||||
initialiseHyperDash(beatmap.HitObjects);
|
initialiseHyperDash(beatmap.HitObjects);
|
||||||
|
|
||||||
|
base.PostProcess(beatmap);
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
foreach (var obj in beatmap.HitObjects)
|
foreach (var obj in beatmap.HitObjects)
|
||||||
{
|
|
||||||
if (obj.NewCombo)
|
|
||||||
{
|
|
||||||
if (lastObj != null) lastObj.LastInCombo = true;
|
|
||||||
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.IndexInBeatmap = index++;
|
obj.IndexInBeatmap = index++;
|
||||||
obj.ComboColour = beatmap.ComboColours[colourIndex];
|
|
||||||
|
|
||||||
lastObj = obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
@ -32,25 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new Banana
|
AddNested(new Banana
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
ComboColour = getNextComboColour(),
|
|
||||||
StartTime = i,
|
StartTime = i,
|
||||||
X = RNG.NextSingle()
|
X = RNG.NextSingle()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 getNextComboColour()
|
|
||||||
{
|
|
||||||
switch (RNG.Next(0, 3))
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
return new Color4(255, 240, 0, 255);
|
|
||||||
case 1:
|
|
||||||
return new Color4(255, 192, 0, 255);
|
|
||||||
case 2:
|
|
||||||
return new Color4(214, 221, 28, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double EndTime => StartTime + Duration;
|
public double EndTime => StartTime + Duration;
|
||||||
|
|
||||||
public double Duration { get; set; }
|
public double Duration { get; set; }
|
||||||
|
@ -5,24 +5,25 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo
|
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
|
||||||
{
|
{
|
||||||
public const double OBJECT_RADIUS = 44;
|
public const double OBJECT_RADIUS = 44;
|
||||||
|
|
||||||
public float X { get; set; }
|
public float X { get; set; }
|
||||||
|
|
||||||
public Color4 ComboColour { get; set; }
|
|
||||||
|
|
||||||
public int IndexInBeatmap { get; set; }
|
public int IndexInBeatmap { get; set; }
|
||||||
|
|
||||||
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
||||||
|
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
|
|
||||||
|
public int IndexInCurrentCombo { get; set; }
|
||||||
|
|
||||||
|
public int ComboIndex { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The next fruit starts a new combo. Used for explodey.
|
/// The next fruit starts a new combo. Used for explodey.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -8,6 +8,8 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
@ -57,6 +59,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
|
{
|
||||||
|
base.SkinChanged(skin, allowFallback);
|
||||||
|
|
||||||
|
if (HitObject is IHasComboInformation combo)
|
||||||
|
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||||
|
}
|
||||||
|
|
||||||
private const float preempt = 1000;
|
private const float preempt = 1000;
|
||||||
|
|
||||||
protected override void UpdateState(ArmedState state)
|
protected override void UpdateState(ArmedState state)
|
||||||
|
@ -5,28 +5,39 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
|
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
|
||||||
{
|
{
|
||||||
|
private Pulp pulp;
|
||||||
|
|
||||||
public DrawableDroplet(Droplet h)
|
public DrawableDroplet(Droplet h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
|
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
|
||||||
AccentColour = h.ComboColour;
|
|
||||||
Masking = false;
|
Masking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = new Pulp
|
InternalChild = pulp = new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
|
||||||
Size = Size
|
Size = Size
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Color4 AccentColour
|
||||||
|
{
|
||||||
|
get { return base.AccentColour; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.AccentColour = value;
|
||||||
|
pulp.AccentColour = AccentColour;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
|
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
|
||||||
AccentColour = HitObject.ComboColour;
|
|
||||||
Masking = false;
|
Masking = false;
|
||||||
|
|
||||||
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||||
@ -33,6 +32,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
// todo: this should come from the skin.
|
||||||
|
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
createPulp(HitObject.VisualRepresentation),
|
createPulp(HitObject.VisualRepresentation),
|
||||||
@ -273,5 +275,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
|
|
||||||
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
||||||
|
{
|
||||||
|
switch (representation)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case FruitVisualRepresentation.Pear:
|
||||||
|
return new Color4(17, 136, 170, 255);
|
||||||
|
case FruitVisualRepresentation.Grape:
|
||||||
|
return new Color4(204, 102, 0, 255);
|
||||||
|
case FruitVisualRepresentation.Raspberry:
|
||||||
|
return new Color4(121, 9, 13, 255);
|
||||||
|
case FruitVisualRepresentation.Pineapple:
|
||||||
|
return new Color4(102, 136, 0, 255);
|
||||||
|
case FruitVisualRepresentation.Banana:
|
||||||
|
switch (RNG.Next(0, 3))
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return new Color4(255, 240, 0, 255);
|
||||||
|
case 1:
|
||||||
|
return new Color4(255, 192, 0, 255);
|
||||||
|
case 2:
|
||||||
|
return new Color4(214, 221, 28, 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
|||||||
var catchObject = (DrawableCatchHitObject)h;
|
var catchObject = (DrawableCatchHitObject)h;
|
||||||
|
|
||||||
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||||
catchObject.AccentColour = HitObject.ComboColour;
|
|
||||||
|
|
||||||
dropletContainer.Add(h);
|
dropletContainer.Add(h);
|
||||||
base.AddNested(h);
|
base.AddNested(h);
|
||||||
|
@ -60,7 +60,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new Fruit
|
AddNested(new Fruit
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
ComboColour = ComboColour,
|
|
||||||
StartTime = StartTime,
|
StartTime = StartTime,
|
||||||
X = X
|
X = X
|
||||||
});
|
});
|
||||||
@ -90,7 +89,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new TinyDroplet
|
AddNested(new TinyDroplet
|
||||||
{
|
{
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
ComboColour = ComboColour,
|
|
||||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||||
{
|
{
|
||||||
@ -106,7 +104,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new Droplet
|
AddNested(new Droplet
|
||||||
{
|
{
|
||||||
StartTime = time,
|
StartTime = time,
|
||||||
ComboColour = ComboColour,
|
|
||||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||||
{
|
{
|
||||||
@ -123,7 +120,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new Fruit
|
AddNested(new Fruit
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
ComboColour = ComboColour,
|
|
||||||
StartTime = spanStartTime + spanDuration,
|
StartTime = spanStartTime + spanDuration,
|
||||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||||
});
|
});
|
||||||
|
@ -6,13 +6,11 @@ using System.Collections.Generic;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.MathUtils;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
@ -62,8 +60,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
Scale = 1.5f,
|
Scale = 1.5f,
|
||||||
};
|
};
|
||||||
|
|
||||||
fruit.ComboColour = colourForRrepesentation(fruit.VisualRepresentation);
|
|
||||||
|
|
||||||
return new DrawableFruit(fruit)
|
return new DrawableFruit(fruit)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -74,31 +70,5 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
LifetimeEnd = double.PositiveInfinity,
|
LifetimeEnd = double.PositiveInfinity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
|
||||||
{
|
|
||||||
switch (representation)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case FruitVisualRepresentation.Pear:
|
|
||||||
return new Color4(17, 136, 170, 255);
|
|
||||||
case FruitVisualRepresentation.Grape:
|
|
||||||
return new Color4(204, 102, 0, 255);
|
|
||||||
case FruitVisualRepresentation.Raspberry:
|
|
||||||
return new Color4(121, 9, 13, 255);
|
|
||||||
case FruitVisualRepresentation.Pineapple:
|
|
||||||
return new Color4(102, 136, 0, 255);
|
|
||||||
case FruitVisualRepresentation.Banana:
|
|
||||||
switch (RNG.Next(0, 3))
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
return new Color4(255, 240, 0, 255);
|
|
||||||
case 1:
|
|
||||||
return new Color4(255, 192, 0, 255);
|
|
||||||
case 2:
|
|
||||||
return new Color4(214, 221, 28, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (caughtFruit == null) return;
|
if (caughtFruit == null) return;
|
||||||
|
|
||||||
caughtFruit.AccentColour = fruit.AccentColour;
|
|
||||||
caughtFruit.RelativePositionAxes = Axes.None;
|
caughtFruit.RelativePositionAxes = Axes.None;
|
||||||
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
|||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
@ -24,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
|
|
||||||
private readonly GlowPiece glowPiece;
|
private readonly GlowPiece glowPiece;
|
||||||
private readonly BodyPiece bodyPiece;
|
private readonly BodyPiece bodyPiece;
|
||||||
private readonly Container<DrawableHoldNoteTick> tickContainer;
|
|
||||||
private readonly Container fullHeightContainer;
|
private readonly Container fullHeightContainer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -40,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
|
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
|
||||||
: base(hitObject, action)
|
: base(hitObject, action)
|
||||||
{
|
{
|
||||||
|
Container<DrawableHoldNoteTick> tickContainer;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
@ -57,7 +56,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
|
tickContainer = new Container<DrawableHoldNoteTick>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ChildrenEnumerable = HitObject.NestedHitObjects.OfType<HoldNoteTick>().Select(tick => new DrawableHoldNoteTick(tick)
|
||||||
|
{
|
||||||
|
HoldStartTime = () => holdStartTime
|
||||||
|
})
|
||||||
|
},
|
||||||
head = new DrawableHeadNote(this, action)
|
head = new DrawableHeadNote(this, action)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
@ -70,16 +76,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var tick in HitObject.NestedHitObjects.OfType<HoldNoteTick>())
|
foreach (var tick in tickContainer)
|
||||||
{
|
AddNested(tick);
|
||||||
var drawableTick = new DrawableHoldNoteTick(tick)
|
|
||||||
{
|
|
||||||
HoldStartTime = () => holdStartTime
|
|
||||||
};
|
|
||||||
|
|
||||||
tickContainer.Add(drawableTick);
|
|
||||||
AddNested(drawableTick);
|
|
||||||
}
|
|
||||||
|
|
||||||
AddNested(head);
|
AddNested(head);
|
||||||
AddNested(tail);
|
AddNested(tail);
|
||||||
@ -90,12 +88,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
get { return base.AccentColour; }
|
get { return base.AccentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (base.AccentColour == value)
|
|
||||||
return;
|
|
||||||
base.AccentColour = value;
|
base.AccentColour = value;
|
||||||
|
|
||||||
tickContainer.Children.ForEach(t => t.AccentColour = value);
|
|
||||||
|
|
||||||
glowPiece.AccentColour = value;
|
glowPiece.AccentColour = value;
|
||||||
bodyPiece.AccentColour = value;
|
bodyPiece.AccentColour = value;
|
||||||
head.AccentColour = value;
|
head.AccentColour = value;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using OpenTK.Graphics;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
@ -28,16 +27,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (action != null)
|
if (action != null)
|
||||||
Action = action.Value;
|
Action = action.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Color4 AccentColour
|
|
||||||
{
|
|
||||||
get { return base.AccentColour; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (base.AccentColour == value)
|
|
||||||
return;
|
|
||||||
base.AccentColour = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
get { return base.AccentColour; }
|
get { return base.AccentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (base.AccentColour == value)
|
|
||||||
return;
|
|
||||||
base.AccentColour = value;
|
base.AccentColour = value;
|
||||||
|
laneGlowPiece.AccentColour = AccentColour;
|
||||||
laneGlowPiece.AccentColour = value;
|
GlowPiece.AccentColour = AccentColour;
|
||||||
GlowPiece.AccentColour = value;
|
headPiece.AccentColour = AccentColour;
|
||||||
headPiece.AccentColour = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,24 +13,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
public override void PostProcess(Beatmap<OsuHitObject> beatmap)
|
public override void PostProcess(Beatmap<OsuHitObject> beatmap)
|
||||||
{
|
{
|
||||||
applyStacking(beatmap);
|
applyStacking(beatmap);
|
||||||
|
base.PostProcess(beatmap);
|
||||||
if (beatmap.ComboColours.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int comboIndex = 0;
|
|
||||||
int colourIndex = 0;
|
|
||||||
|
|
||||||
foreach (var obj in beatmap.HitObjects)
|
|
||||||
{
|
|
||||||
if (obj.NewCombo)
|
|
||||||
{
|
|
||||||
comboIndex = 0;
|
|
||||||
colourIndex = (colourIndex + 1) % beatmap.ComboColours.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.IndexInCurrentCombo = comboIndex++;
|
|
||||||
obj.ComboColour = beatmap.ComboColours[colourIndex];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyStacking(Beatmap<OsuHitObject> beatmap)
|
private void applyStacking(Beatmap<OsuHitObject> beatmap)
|
||||||
|
@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
|||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -21,7 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private readonly NumberPiece number;
|
private readonly NumberPiece number;
|
||||||
private readonly GlowPiece glow;
|
private readonly GlowPiece glow;
|
||||||
|
|
||||||
public DrawableHitCircle(HitCircle h) : base(h)
|
public DrawableHitCircle(HitCircle h)
|
||||||
|
: base(h)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
@ -30,13 +32,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
glow = new GlowPiece
|
glow = new GlowPiece(),
|
||||||
{
|
|
||||||
Colour = AccentColour
|
|
||||||
},
|
|
||||||
circle = new CirclePiece
|
circle = new CirclePiece
|
||||||
{
|
{
|
||||||
Colour = AccentColour,
|
|
||||||
Hit = () =>
|
Hit = () =>
|
||||||
{
|
{
|
||||||
if (AllJudged)
|
if (AllJudged)
|
||||||
@ -52,15 +50,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
},
|
},
|
||||||
ring = new RingPiece(),
|
ring = new RingPiece(),
|
||||||
flash = new FlashPiece(),
|
flash = new FlashPiece(),
|
||||||
explode = new ExplodePiece
|
explode = new ExplodePiece(),
|
||||||
{
|
|
||||||
Colour = AccentColour,
|
|
||||||
},
|
|
||||||
ApproachCircle = new ApproachCircle
|
ApproachCircle = new ApproachCircle
|
||||||
{
|
{
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
Scale = new Vector2(4),
|
Scale = new Vector2(4),
|
||||||
Colour = AccentColour,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,6 +64,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Color4 AccentColour
|
||||||
|
{
|
||||||
|
get { return base.AccentColour; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.AccentColour = value;
|
||||||
|
explode.Colour = AccentColour;
|
||||||
|
glow.Colour = AccentColour;
|
||||||
|
circle.Colour = AccentColour;
|
||||||
|
ApproachCircle.Colour = AccentColour;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
|
@ -5,6 +5,9 @@ using System.ComponentModel;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -15,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
AccentColour = HitObject.ComboColour;
|
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +37,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
|
{
|
||||||
|
base.SkinChanged(skin, allowFallback);
|
||||||
|
|
||||||
|
if (HitObject is IHasComboInformation combo)
|
||||||
|
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
|
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
|
||||||
|
|
||||||
protected virtual void UpdateCurrentState(ArmedState state)
|
protected virtual void UpdateCurrentState(ArmedState state)
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Osu.Judgements;
|
|||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -41,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
Body = new SliderBody(s)
|
Body = new SliderBody(s)
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
|
||||||
PathWidth = s.Scale * 64,
|
PathWidth = s.Scale * 64,
|
||||||
},
|
},
|
||||||
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||||
@ -50,7 +50,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
BypassAutoSizeAxes = Axes.Both,
|
BypassAutoSizeAxes = Axes.Both,
|
||||||
Scale = new Vector2(s.Scale),
|
Scale = new Vector2(s.Scale),
|
||||||
AccentColour = AccentColour,
|
|
||||||
AlwaysPresent = true,
|
AlwaysPresent = true,
|
||||||
Alpha = 0
|
Alpha = 0
|
||||||
},
|
},
|
||||||
@ -87,6 +86,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Color4 AccentColour
|
||||||
|
{
|
||||||
|
get { return base.AccentColour; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.AccentColour = value;
|
||||||
|
Body.AccentColour = AccentColour;
|
||||||
|
Ball.AccentColour = AccentColour;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Blending = BlendingMode.Additive,
|
Blending = BlendingMode.Additive,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0.2f,
|
Alpha = 0.2f,
|
||||||
}, false);
|
}, s => s.GetTexture("Play/osu/hitcircle") == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
}
|
}
|
||||||
}, false);
|
}, s => s.GetTexture("Play/osu/hitcircle") == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Texture = textures.Get(name),
|
Texture = textures.Get(name),
|
||||||
Blending = BlendingMode.Additive,
|
Blending = BlendingMode.Additive,
|
||||||
Alpha = 0.5f
|
Alpha = 0.5f
|
||||||
}, false);
|
}, s => s.GetTexture("Play/osu/hitcircle") == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Colour = Color4.White.Opacity(0.5f),
|
Colour = Color4.White.Opacity(0.5f),
|
||||||
},
|
},
|
||||||
Child = new Box()
|
Child = new Box()
|
||||||
}, false),
|
}, s => s.GetTexture("Play/osu/hitcircle") == null),
|
||||||
number = new OsuSpriteText
|
number = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = @"1",
|
Text = @"1",
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
public double? SnakedStart { get; private set; }
|
public double? SnakedStart { get; private set; }
|
||||||
public double? SnakedEnd { get; private set; }
|
public double? SnakedEnd { get; private set; }
|
||||||
|
|
||||||
private Color4 accentColour;
|
private Color4 accentColour = Color4.White;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to colour the path.
|
/// Used to colour the path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -173,6 +173,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
|
|
||||||
texture.SetData(upload);
|
texture.SetData(upload);
|
||||||
path.Texture = texture;
|
path.Texture = texture;
|
||||||
|
|
||||||
|
container.ForceRedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void computeSize()
|
private void computeSize()
|
||||||
|
@ -6,13 +6,11 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK.Graphics;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Edit.Types;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
public abstract class OsuHitObject : HitObject, IHasCombo, IHasEditablePosition
|
public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition
|
||||||
{
|
{
|
||||||
public const double OBJECT_RADIUS = 64;
|
public const double OBJECT_RADIUS = 64;
|
||||||
|
|
||||||
@ -53,10 +51,14 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public float Scale { get; set; } = 1;
|
public float Scale { get; set; } = 1;
|
||||||
|
|
||||||
public Color4 ComboColour { get; set; } = Color4.Gray;
|
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
|
|
||||||
public int IndexInCurrentCombo { get; set; }
|
public int IndexInCurrentCombo { get; set; }
|
||||||
|
|
||||||
|
public int ComboIndex { get; set; }
|
||||||
|
|
||||||
|
public bool LastInCombo { get; set; }
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
@ -99,10 +99,10 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
StartTime = StartTime,
|
StartTime = StartTime,
|
||||||
Position = Position,
|
Position = Position,
|
||||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
|
||||||
ComboColour = ComboColour,
|
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
SampleControlPoint = SampleControlPoint
|
SampleControlPoint = SampleControlPoint,
|
||||||
|
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||||
|
ComboIndex = ComboIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
TailCircle = new SliderCircle(this)
|
TailCircle = new SliderCircle(this)
|
||||||
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
StartTime = EndTime,
|
StartTime = EndTime,
|
||||||
Position = EndPosition,
|
Position = EndPosition,
|
||||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||||
ComboColour = ComboColour
|
ComboIndex = ComboIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
AddNested(HeadCircle);
|
AddNested(HeadCircle);
|
||||||
@ -160,7 +160,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
Position = Position + Curve.PositionAt(distanceProgress),
|
Position = Position + Curve.PositionAt(distanceProgress),
|
||||||
StackHeight = StackHeight,
|
StackHeight = StackHeight,
|
||||||
Scale = Scale,
|
Scale = Scale,
|
||||||
ComboColour = ComboColour,
|
|
||||||
Samples = sampleList
|
Samples = sampleList
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -179,7 +178,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
Position = Position + Curve.PositionAt(repeat % 2),
|
Position = Position + Curve.PositionAt(repeat % 2),
|
||||||
StackHeight = StackHeight,
|
StackHeight = StackHeight,
|
||||||
Scale = Scale,
|
Scale = Scale,
|
||||||
ComboColour = ComboColour,
|
|
||||||
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
|
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System;
|
||||||
@ -61,7 +60,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = Time.Current + 1000 + timeOffset,
|
StartTime = Time.Current + 1000 + timeOffset,
|
||||||
Position = positionOffset.Value,
|
Position = positionOffset.Value,
|
||||||
ComboColour = Color4.LightSeaGreen
|
|
||||||
};
|
};
|
||||||
|
|
||||||
circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
|
circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
|
||||||
|
@ -117,7 +117,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-(distance / 2), 0),
|
Position = new Vector2(-(distance / 2), 0),
|
||||||
ComboColour = Color4.LightSeaGreen,
|
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new List<Vector2>
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
@ -138,7 +137,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-200, 0),
|
Position = new Vector2(-200, 0),
|
||||||
ComboColour = Color4.LightSeaGreen,
|
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new List<Vector2>
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
@ -162,7 +160,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
CurveType = CurveType.Linear,
|
CurveType = CurveType.Linear,
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-200, 0),
|
Position = new Vector2(-200, 0),
|
||||||
ComboColour = Color4.LightSeaGreen,
|
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new List<Vector2>
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
@ -189,7 +186,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
CurveType = CurveType.Bezier,
|
CurveType = CurveType.Bezier,
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-200, 0),
|
Position = new Vector2(-200, 0),
|
||||||
ComboColour = Color4.LightSeaGreen,
|
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new List<Vector2>
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
@ -215,7 +211,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
CurveType = CurveType.Linear,
|
CurveType = CurveType.Linear,
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(0, 0),
|
Position = new Vector2(0, 0),
|
||||||
ComboColour = Color4.LightSeaGreen,
|
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new List<Vector2>
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
@ -245,7 +240,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-100, 0),
|
Position = new Vector2(-100, 0),
|
||||||
ComboColour = Color4.LightSeaGreen,
|
|
||||||
CurveType = CurveType.Catmull,
|
CurveType = CurveType.Catmull,
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new List<Vector2>
|
||||||
{
|
{
|
||||||
|
@ -20,11 +20,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
public class DrawableDrumRoll : DrawableTaikoHitObject<DrumRoll>
|
public class DrawableDrumRoll : DrawableTaikoHitObject<DrumRoll>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of rolling hits required to reach the dark/final accent colour.
|
/// Number of rolling hits required to reach the dark/final colour.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int rolling_hits_for_dark_accent = 5;
|
private const int rolling_hits_for_engaged_colour = 5;
|
||||||
|
|
||||||
private Color4 accentDarkColour;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rolling number of tick hits. This increases for hits and decreases for misses.
|
/// Rolling number of tick hits. This increases for hits and decreases for misses.
|
||||||
@ -53,11 +51,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
public override bool OnPressed(TaikoAction action) => false;
|
public override bool OnPressed(TaikoAction action) => false;
|
||||||
|
|
||||||
|
private Color4 colourIdle;
|
||||||
|
private Color4 colourEngaged;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
MainPiece.AccentColour = AccentColour = colours.YellowDark;
|
MainPiece.AccentColour = colourIdle = colours.YellowDark;
|
||||||
accentDarkColour = colours.YellowDarker;
|
colourEngaged = colours.YellowDarker;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
|
private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
|
||||||
@ -67,10 +68,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
else
|
else
|
||||||
rollingHits--;
|
rollingHits--;
|
||||||
|
|
||||||
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent);
|
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_engaged_colour);
|
||||||
|
|
||||||
Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1);
|
Color4 newColour = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_engaged_colour, colourIdle, colourEngaged, 0, 1);
|
||||||
MainPiece.FadeAccent(newAccent, 100);
|
MainPiece.FadeAccent(newColour, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Audio;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Beatmaps.Formats
|
namespace osu.Game.Tests.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
@ -163,7 +164,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeBeatmapColors()
|
public void TestDecodeBeatmapColors()
|
||||||
{
|
{
|
||||||
var decoder = new LegacyBeatmapDecoder();
|
var decoder = new LegacySkinDecoder();
|
||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
using (var stream = new StreamReader(resStream))
|
using (var stream = new StreamReader(resStream))
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,6 @@ using osu.Game.IO.Serialization;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Beatmaps.Formats
|
namespace osu.Game.Tests.Beatmaps.Formats
|
||||||
{
|
{
|
||||||
@ -89,24 +88,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual(2, difficulty.SliderTickRate);
|
Assert.AreEqual(2, difficulty.SliderTickRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestDecodeColors()
|
|
||||||
{
|
|
||||||
var beatmap = decodeAsJson(normal);
|
|
||||||
Color4[] expected =
|
|
||||||
{
|
|
||||||
new Color4(142, 199, 255, 255),
|
|
||||||
new Color4(255, 128, 128, 255),
|
|
||||||
new Color4(128, 255, 255, 255),
|
|
||||||
new Color4(128, 255, 128, 255),
|
|
||||||
new Color4(255, 187, 255, 255),
|
|
||||||
new Color4(255, 177, 140, 255),
|
|
||||||
};
|
|
||||||
Assert.AreEqual(expected.Length, beatmap.ComboColours.Count);
|
|
||||||
for (int i = 0; i < expected.Length; i++)
|
|
||||||
Assert.AreEqual(expected[i], beatmap.ComboColours[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeHitObjects()
|
public void TestDecodeHitObjects()
|
||||||
{
|
{
|
||||||
|
28
osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs
Normal file
28
osu.Game.Tests/Visual/TestCaseBeatDivisorControl.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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Screens.Edit.Screens.Compose;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual
|
||||||
|
{
|
||||||
|
public class TestCaseBeatDivisorControl : OsuTestCase
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BindableBeatDivisor) };
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new BeatDivisorControl(new BindableBeatDivisor())
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(90, 90)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
@ -13,6 +15,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseEditorCompose : OsuTestCase
|
public class TestCaseEditorCompose : OsuTestCase
|
||||||
{
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Compose) };
|
||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
@ -18,6 +18,7 @@ using osu.Game.Rulesets.Edit;
|
|||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Screens.Edit.Screens.Compose;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
@ -31,6 +32,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
private Track track;
|
private Track track;
|
||||||
private HitObjectComposer composer;
|
private HitObjectComposer composer;
|
||||||
|
|
||||||
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4);
|
||||||
|
|
||||||
private DecoupleableInterpolatingFramedClock clock;
|
private DecoupleableInterpolatingFramedClock clock;
|
||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
@ -44,6 +47,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||||
dependencies.CacheAs<IAdjustableClock>(clock);
|
dependencies.CacheAs<IAdjustableClock>(clock);
|
||||||
dependencies.CacheAs<IFrameBasedClock>(clock);
|
dependencies.CacheAs<IFrameBasedClock>(clock);
|
||||||
|
dependencies.Cache(beatDivisor);
|
||||||
|
|
||||||
var testBeatmap = new Beatmap
|
var testBeatmap = new Beatmap
|
||||||
{
|
{
|
||||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext());
|
IDatabaseContextFactory factory = new SingletonContextFactory(new OsuDbContext());
|
||||||
|
|
||||||
dependencies.Cache(rulesets = new RulesetStore(factory));
|
dependencies.Cache(rulesets = new RulesetStore(factory));
|
||||||
dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null)
|
dependencies.Cache(manager = new BeatmapManager(storage, factory, rulesets, null, null)
|
||||||
{
|
{
|
||||||
DefaultBeatmap = defaultBeatmap = game.Beatmap.Default
|
DefaultBeatmap = defaultBeatmap = game.Beatmap.Default
|
||||||
});
|
});
|
||||||
|
@ -116,6 +116,7 @@
|
|||||||
<Compile Include="Visual\TestCaseBeatmapOptionsOverlay.cs" />
|
<Compile Include="Visual\TestCaseBeatmapOptionsOverlay.cs" />
|
||||||
<Compile Include="Visual\TestCaseBeatmapScoresContainer.cs" />
|
<Compile Include="Visual\TestCaseBeatmapScoresContainer.cs" />
|
||||||
<Compile Include="Visual\TestCaseBeatmapSetOverlay.cs" />
|
<Compile Include="Visual\TestCaseBeatmapSetOverlay.cs" />
|
||||||
|
<Compile Include="Visual\TestCaseBeatDivisorControl.cs" />
|
||||||
<Compile Include="Visual\TestCaseBeatSyncedContainer.cs" />
|
<Compile Include="Visual\TestCaseBeatSyncedContainer.cs" />
|
||||||
<Compile Include="Visual\TestCaseBreadcrumbs.cs" />
|
<Compile Include="Visual\TestCaseBreadcrumbs.cs" />
|
||||||
<Compile Include="Visual\TestCaseBreakOverlay.cs" />
|
<Compile Include="Visual\TestCaseBreakOverlay.cs" />
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -9,7 +8,6 @@ using System.Linq;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.IO.Serialization;
|
using osu.Game.IO.Serialization;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Game.Beatmaps.Formats;
|
|
||||||
using osu.Game.IO.Serialization.Converters;
|
using osu.Game.IO.Serialization.Converters;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
@ -17,21 +15,13 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Beatmap containing converted HitObjects.
|
/// A Beatmap containing converted HitObjects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Beatmap<T> : IJsonSerializable, IHasComboColours
|
public class Beatmap<T> : IJsonSerializable
|
||||||
where T : HitObject
|
where T : HitObject
|
||||||
{
|
{
|
||||||
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
public BeatmapInfo BeatmapInfo = new BeatmapInfo();
|
||||||
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
|
||||||
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
|
||||||
|
|
||||||
public List<Color4> ComboColours { get; set; } = new List<Color4>
|
|
||||||
{
|
|
||||||
new Color4(17, 136, 170, 255),
|
|
||||||
new Color4(102, 136, 0, 255),
|
|
||||||
new Color4(204, 102, 0, 255),
|
|
||||||
new Color4(121, 9, 13, 255)
|
|
||||||
};
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata;
|
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata;
|
||||||
|
|
||||||
@ -56,7 +46,6 @@ namespace osu.Game.Beatmaps
|
|||||||
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
|
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
|
||||||
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
|
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
|
||||||
Breaks = original?.Breaks ?? Breaks;
|
Breaks = original?.Breaks ?? Breaks;
|
||||||
ComboColours = original?.ComboColours ?? ComboColours;
|
|
||||||
HitObjects = original?.HitObjects ?? HitObjects;
|
HitObjects = original?.HitObjects ?? HitObjects;
|
||||||
|
|
||||||
if (original == null && Metadata == null)
|
if (original == null && Metadata == null)
|
||||||
|
@ -57,7 +57,6 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmap.ControlPointInfo = original.ControlPointInfo;
|
beatmap.ControlPointInfo = original.ControlPointInfo;
|
||||||
beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
|
beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
|
||||||
beatmap.Breaks = original.Breaks;
|
beatmap.Breaks = original.Breaks;
|
||||||
beatmap.ComboColours = original.ComboColours;
|
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
@ -55,6 +56,8 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private readonly APIAccess api;
|
private readonly APIAccess api;
|
||||||
|
|
||||||
|
private readonly AudioManager audioManager;
|
||||||
|
|
||||||
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
private readonly List<DownloadBeatmapSetRequest> currentDownloads = new List<DownloadBeatmapSetRequest>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -62,7 +65,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<Storage> GetStableStorage { private get; set; }
|
public Func<Storage> GetStableStorage { private get; set; }
|
||||||
|
|
||||||
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
|
public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null)
|
||||||
: base(storage, contextFactory, new BeatmapStore(contextFactory), importHost)
|
: base(storage, contextFactory, new BeatmapStore(contextFactory), importHost)
|
||||||
{
|
{
|
||||||
beatmaps = (BeatmapStore)ModelStore;
|
beatmaps = (BeatmapStore)ModelStore;
|
||||||
@ -71,6 +74,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
this.rulesets = rulesets;
|
this.rulesets = rulesets;
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
this.audioManager = audioManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
|
protected override void Populate(BeatmapSetInfo model, ArchiveReader archive)
|
||||||
@ -217,7 +221,7 @@ namespace osu.Game.Beatmaps
|
|||||||
if (beatmapInfo.Metadata == null)
|
if (beatmapInfo.Metadata == null)
|
||||||
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
|
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
|
||||||
|
|
||||||
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo);
|
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo, audioManager);
|
||||||
|
|
||||||
previous?.TransferTo(working);
|
previous?.TransferTo(working);
|
||||||
|
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Graphics.Textures;
|
using osu.Game.Graphics.Textures;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Storyboards;
|
using osu.Game.Storyboards;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
@ -19,11 +21,13 @@ namespace osu.Game.Beatmaps
|
|||||||
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
|
protected class BeatmapManagerWorkingBeatmap : WorkingBeatmap
|
||||||
{
|
{
|
||||||
private readonly IResourceStore<byte[]> store;
|
private readonly IResourceStore<byte[]> store;
|
||||||
|
private readonly AudioManager audioManager;
|
||||||
|
|
||||||
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo)
|
public BeatmapManagerWorkingBeatmap(IResourceStore<byte[]> store, BeatmapInfo beatmapInfo, AudioManager audioManager)
|
||||||
: base(beatmapInfo)
|
: base(beatmapInfo)
|
||||||
{
|
{
|
||||||
this.store = store;
|
this.store = store;
|
||||||
|
this.audioManager = audioManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap GetBeatmap()
|
protected override Beatmap GetBeatmap()
|
||||||
@ -100,6 +104,22 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
return storyboard;
|
return storyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Skin GetSkin()
|
||||||
|
{
|
||||||
|
Skin skin;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error(e, "Skin failed to load");
|
||||||
|
skin = new DefaultSkin();
|
||||||
|
}
|
||||||
|
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
@ -19,6 +21,29 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmap">The Beatmap to process.</param>
|
/// <param name="beatmap">The Beatmap to process.</param>
|
||||||
public virtual void PostProcess(Beatmap<TObject> beatmap) { }
|
public virtual void PostProcess(Beatmap<TObject> beatmap)
|
||||||
|
{
|
||||||
|
IHasComboInformation lastObj = null;
|
||||||
|
|
||||||
|
foreach (var obj in beatmap.HitObjects.OfType<IHasComboInformation>())
|
||||||
|
{
|
||||||
|
if (obj.NewCombo)
|
||||||
|
{
|
||||||
|
obj.IndexInCurrentCombo = 0;
|
||||||
|
if (lastObj != null)
|
||||||
|
{
|
||||||
|
lastObj.LastInCombo = true;
|
||||||
|
obj.ComboIndex = lastObj.ComboIndex + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (lastObj != null)
|
||||||
|
{
|
||||||
|
obj.IndexInCurrentCombo = lastObj.IndexInCurrentCombo + 1;
|
||||||
|
obj.ComboIndex = lastObj.ComboIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastObj = obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private void handleGeneral(string line)
|
private void handleGeneral(string line)
|
||||||
{
|
{
|
||||||
var pair = SplitKeyVal(line, ':');
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
var metadata = beatmap.BeatmapInfo.Metadata;
|
var metadata = beatmap.BeatmapInfo.Metadata;
|
||||||
switch (pair.Key)
|
switch (pair.Key)
|
||||||
@ -155,7 +155,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private void handleEditor(string line)
|
private void handleEditor(string line)
|
||||||
{
|
{
|
||||||
var pair = SplitKeyVal(line, ':');
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
switch (pair.Key)
|
switch (pair.Key)
|
||||||
{
|
{
|
||||||
@ -179,7 +179,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private void handleMetadata(string line)
|
private void handleMetadata(string line)
|
||||||
{
|
{
|
||||||
var pair = SplitKeyVal(line, ':');
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
var metadata = beatmap.BeatmapInfo.Metadata;
|
var metadata = beatmap.BeatmapInfo.Metadata;
|
||||||
switch (pair.Key)
|
switch (pair.Key)
|
||||||
@ -220,7 +220,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private void handleDifficulty(string line)
|
private void handleDifficulty(string line)
|
||||||
{
|
{
|
||||||
var pair = SplitKeyVal(line, ':');
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||||
switch (pair.Key)
|
switch (pair.Key)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
@ -31,7 +32,11 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
|
||||||
{
|
{
|
||||||
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
|
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
|
||||||
throw new InvalidDataException($@"Unknown osu section {line}");
|
{
|
||||||
|
Logger.Log($"Unknown section \"{line}\" in {beatmap}");
|
||||||
|
section = Section.None;
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +60,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private void handleColours(T output, string line)
|
private void handleColours(T output, string line)
|
||||||
{
|
{
|
||||||
var pair = SplitKeyVal(line, ':');
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
bool isCombo = pair.Key.StartsWith(@"Combo");
|
bool isCombo = pair.Key.StartsWith(@"Combo");
|
||||||
|
|
||||||
@ -89,7 +94,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator)
|
protected KeyValuePair<string, string> SplitKeyVal(string line, char separator = ':')
|
||||||
{
|
{
|
||||||
var split = line.Trim().Split(new[] { separator }, 2);
|
var split = line.Trim().Split(new[] { separator }, 2);
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.IO.File;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Game.IO.Serialization;
|
using osu.Game.IO.Serialization;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
@ -40,6 +41,7 @@ namespace osu.Game.Beatmaps
|
|||||||
track = new AsyncLazy<Track>(populateTrack);
|
track = new AsyncLazy<Track>(populateTrack);
|
||||||
waveform = new AsyncLazy<Waveform>(populateWaveform);
|
waveform = new AsyncLazy<Waveform>(populateWaveform);
|
||||||
storyboard = new AsyncLazy<Storyboard>(populateStoryboard);
|
storyboard = new AsyncLazy<Storyboard>(populateStoryboard);
|
||||||
|
skin = new AsyncLazy<Skin>(populateSkin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -56,6 +58,7 @@ namespace osu.Game.Beatmaps
|
|||||||
protected abstract Beatmap GetBeatmap();
|
protected abstract Beatmap GetBeatmap();
|
||||||
protected abstract Texture GetBackground();
|
protected abstract Texture GetBackground();
|
||||||
protected abstract Track GetTrack();
|
protected abstract Track GetTrack();
|
||||||
|
protected virtual Skin GetSkin() => new DefaultSkin();
|
||||||
protected virtual Waveform GetWaveform() => new Waveform();
|
protected virtual Waveform GetWaveform() => new Waveform();
|
||||||
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
|
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
|
||||||
|
|
||||||
@ -109,6 +112,13 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private Storyboard populateStoryboard() => GetStoryboard();
|
private Storyboard populateStoryboard() => GetStoryboard();
|
||||||
|
|
||||||
|
public bool SkinLoaded => skin.IsResultAvailable;
|
||||||
|
public Skin Skin => skin.Value.Result;
|
||||||
|
public async Task<Skin> GetSkinAsync() => await skin.Value;
|
||||||
|
private readonly AsyncLazy<Skin> skin;
|
||||||
|
|
||||||
|
private Skin populateSkin() => GetSkin();
|
||||||
|
|
||||||
public void TransferTo(WorkingBeatmap other)
|
public void TransferTo(WorkingBeatmap other)
|
||||||
{
|
{
|
||||||
if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
||||||
@ -123,6 +133,7 @@ namespace osu.Game.Beatmaps
|
|||||||
if (BackgroundLoaded) Background?.Dispose();
|
if (BackgroundLoaded) Background?.Dispose();
|
||||||
if (WaveformLoaded) Waveform?.Dispose();
|
if (WaveformLoaded) Waveform?.Dispose();
|
||||||
if (StoryboardLoaded) Storyboard?.Dispose();
|
if (StoryboardLoaded) Storyboard?.Dispose();
|
||||||
|
if (SkinLoaded) Skin?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -82,6 +82,8 @@ namespace osu.Game.Configuration
|
|||||||
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
Set(OsuSetting.ReleaseStream, ReleaseStream.Lazer);
|
||||||
|
|
||||||
Set(OsuSetting.Version, string.Empty);
|
Set(OsuSetting.Version, string.Empty);
|
||||||
|
|
||||||
|
Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsuConfigManager(Storage storage) : base(storage)
|
public OsuConfigManager(Storage storage) : base(storage)
|
||||||
@ -125,6 +127,7 @@ namespace osu.Game.Configuration
|
|||||||
Version,
|
Version,
|
||||||
ShowConvertedBeatmaps,
|
ShowConvertedBeatmaps,
|
||||||
SpeedChangeVisualisation,
|
SpeedChangeVisualisation,
|
||||||
Skin
|
Skin,
|
||||||
|
ScreenshotFormat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ namespace osu.Game.Configuration
|
|||||||
{
|
{
|
||||||
public enum ScreenshotFormat
|
public enum ScreenshotFormat
|
||||||
{
|
{
|
||||||
Bmp = 0, // TODO: Figure out the best way to hide this from the dropdown
|
|
||||||
[Description("JPG (web-friendly)")]
|
[Description("JPG (web-friendly)")]
|
||||||
Jpg = 1,
|
Jpg = 1,
|
||||||
[Description("PNG (lossless)")]
|
[Description("PNG (lossless)")]
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.IO;
|
|||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using SharpCompress.Common;
|
||||||
using FileInfo = osu.Game.IO.FileInfo;
|
using FileInfo = osu.Game.IO.FileInfo;
|
||||||
|
|
||||||
namespace osu.Game.Database
|
namespace osu.Game.Database
|
||||||
@ -79,7 +80,6 @@ namespace osu.Game.Database
|
|||||||
var notification = new ProgressNotification
|
var notification = new ProgressNotification
|
||||||
{
|
{
|
||||||
Text = "Import is initialising...",
|
Text = "Import is initialising...",
|
||||||
CompletionText = "Import successful!",
|
|
||||||
Progress = 0,
|
Progress = 0,
|
||||||
State = ProgressNotificationState.Active,
|
State = ProgressNotificationState.Active,
|
||||||
};
|
};
|
||||||
@ -88,7 +88,8 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
List<TModel> imported = new List<TModel>();
|
List<TModel> imported = new List<TModel>();
|
||||||
|
|
||||||
int i = 0;
|
int current = 0;
|
||||||
|
int errors = 0;
|
||||||
foreach (string path in paths)
|
foreach (string path in paths)
|
||||||
{
|
{
|
||||||
if (notification.State == ProgressNotificationState.Cancelled)
|
if (notification.State == ProgressNotificationState.Cancelled)
|
||||||
@ -97,11 +98,11 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}";
|
notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}";
|
||||||
using (ArchiveReader reader = getReaderFrom(path))
|
using (ArchiveReader reader = getReaderFrom(path))
|
||||||
imported.Add(Import(reader));
|
imported.Add(Import(reader));
|
||||||
|
|
||||||
notification.Progress = (float)++i / paths.Length;
|
notification.Progress = (float)current / paths.Length;
|
||||||
|
|
||||||
// We may or may not want to delete the file depending on where it is stored.
|
// We may or may not want to delete the file depending on where it is stored.
|
||||||
// e.g. reconstructing/repairing database with items from default storage.
|
// e.g. reconstructing/repairing database with items from default storage.
|
||||||
@ -121,9 +122,11 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
e = e.InnerException ?? e;
|
e = e.InnerException ?? e;
|
||||||
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
|
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
|
||||||
|
errors++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!";
|
||||||
notification.State = ProgressNotificationState.Completed;
|
notification.State = ProgressNotificationState.Completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,9 +221,11 @@ namespace osu.Game.Database
|
|||||||
// user requested abort
|
// user requested abort
|
||||||
return;
|
return;
|
||||||
|
|
||||||
notification.Text = $"Deleting ({i} of {items.Count})";
|
notification.Text = $"Deleting ({++i} of {items.Count})";
|
||||||
notification.Progress = (float)++i / items.Count;
|
|
||||||
Delete(b);
|
Delete(b);
|
||||||
|
|
||||||
|
notification.Progress = (float)i / items.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,9 +259,11 @@ namespace osu.Game.Database
|
|||||||
// user requested abort
|
// user requested abort
|
||||||
return;
|
return;
|
||||||
|
|
||||||
notification.Text = $"Restoring ({i} of {items.Count})";
|
notification.Text = $"Restoring ({++i} of {items.Count})";
|
||||||
notification.Progress = (float)++i / items.Count;
|
|
||||||
Undelete(item);
|
Undelete(item);
|
||||||
|
|
||||||
|
notification.Progress = (float)i / items.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +338,9 @@ namespace osu.Game.Database
|
|||||||
{
|
{
|
||||||
if (ZipFile.IsZipFile(path))
|
if (ZipFile.IsZipFile(path))
|
||||||
return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
|
return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
|
||||||
return new LegacyFilesystemReader(path);
|
if (Directory.Exists(path))
|
||||||
|
return new LegacyFilesystemReader(path);
|
||||||
|
throw new InvalidFormatException($"{path} is not a valid archive");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ namespace osu.Game.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TFile">The model representing a file.</typeparam>
|
/// <typeparam name="TFile">The model representing a file.</typeparam>
|
||||||
public interface IHasFiles<TFile>
|
public interface IHasFiles<TFile>
|
||||||
|
where TFile : INamedFileInfo
|
||||||
|
|
||||||
{
|
{
|
||||||
List<TFile> Files { get; set; }
|
List<TFile> Files { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
triangle,
|
triangle,
|
||||||
colourInfo,
|
colourInfo,
|
||||||
null,
|
null,
|
||||||
Shared.VertexBatch.Add,
|
Shared.VertexBatch.AddAction,
|
||||||
Vector2.Divide(localInflationAmount, size));
|
Vector2.Divide(localInflationAmount, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
osu.Game/Graphics/ScreenshotManager.cs
Normal file
110
osu.Game/Graphics/ScreenshotManager.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// 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.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics
|
||||||
|
{
|
||||||
|
public class ScreenshotManager : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||||
|
{
|
||||||
|
private Bindable<ScreenshotFormat> screenshotFormat;
|
||||||
|
private GameHost host;
|
||||||
|
private Storage storage;
|
||||||
|
private NotificationOverlay notificationOverlay;
|
||||||
|
|
||||||
|
private SampleChannel shutter;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(GameHost host, OsuConfigManager config, Storage storage, NotificationOverlay notificationOverlay, AudioManager audio)
|
||||||
|
{
|
||||||
|
this.host = host;
|
||||||
|
this.storage = storage.GetStorageForDirectory(@"screenshots");
|
||||||
|
this.notificationOverlay = notificationOverlay;
|
||||||
|
|
||||||
|
screenshotFormat = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat);
|
||||||
|
|
||||||
|
shutter = audio.Sample.Get("UI/shutter");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GlobalAction.TakeScreenshot:
|
||||||
|
shutter.Play();
|
||||||
|
TakeScreenshotAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
|
|
||||||
|
public async void TakeScreenshotAsync()
|
||||||
|
{
|
||||||
|
using (var bitmap = await host.TakeScreenshotAsync())
|
||||||
|
{
|
||||||
|
var fileName = getFileName();
|
||||||
|
if (fileName == null) return;
|
||||||
|
|
||||||
|
var stream = storage.GetStream(fileName, FileAccess.Write);
|
||||||
|
|
||||||
|
switch (screenshotFormat.Value)
|
||||||
|
{
|
||||||
|
case ScreenshotFormat.Png:
|
||||||
|
bitmap.Save(stream, ImageFormat.Png);
|
||||||
|
break;
|
||||||
|
case ScreenshotFormat.Jpg:
|
||||||
|
bitmap.Save(stream, ImageFormat.Jpeg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(screenshotFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationOverlay.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = $"{fileName} saved!",
|
||||||
|
Activated = () =>
|
||||||
|
{
|
||||||
|
storage.OpenInNativeExplorer();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getFileName()
|
||||||
|
{
|
||||||
|
var dt = DateTime.Now;
|
||||||
|
var fileExt = screenshotFormat.ToString().ToLower();
|
||||||
|
|
||||||
|
var withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}";
|
||||||
|
if (!storage.Exists(withoutIndex))
|
||||||
|
return withoutIndex;
|
||||||
|
|
||||||
|
for (ulong i = 1; i < ulong.MaxValue; i++)
|
||||||
|
{
|
||||||
|
var indexedName = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}-{i}.{fileExt}";
|
||||||
|
if (!storage.Exists(indexedName))
|
||||||
|
return indexedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,9 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We may not be focused yet, but we need to handle keyboard input to be able to request focus
|
||||||
|
public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput;
|
||||||
|
|
||||||
protected override void OnFocus(InputState state)
|
protected override void OnFocus(InputState state)
|
||||||
{
|
{
|
||||||
base.OnFocus(state);
|
base.OnFocus(state);
|
||||||
|
@ -26,6 +26,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
{
|
{
|
||||||
new KeyBinding(InputKey.F8, GlobalAction.ToggleChat),
|
new KeyBinding(InputKey.F8, GlobalAction.ToggleChat),
|
||||||
new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial),
|
new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial),
|
||||||
|
new KeyBinding(InputKey.F12,GlobalAction.TakeScreenshot),
|
||||||
|
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
|
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
|
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
|
||||||
@ -72,5 +74,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
SkipCutscene,
|
SkipCutscene,
|
||||||
[Description("Quick Retry (Hold)")]
|
[Description("Quick Retry (Hold)")]
|
||||||
QuickRetry,
|
QuickRetry,
|
||||||
|
|
||||||
|
[Description("Take screenshot")]
|
||||||
|
TakeScreenshot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,6 @@ namespace osu.Game
|
|||||||
|
|
||||||
// bind config int to database SkinInfo
|
// bind config int to database SkinInfo
|
||||||
configSkin = LocalConfig.GetBindable<int>(OsuSetting.Skin);
|
configSkin = LocalConfig.GetBindable<int>(OsuSetting.Skin);
|
||||||
|
|
||||||
SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID;
|
SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID;
|
||||||
configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default;
|
configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default;
|
||||||
configSkin.TriggerChange();
|
configSkin.TriggerChange();
|
||||||
@ -240,6 +239,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add);
|
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add);
|
||||||
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
|
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
|
||||||
|
loadComponentSingleFile(new ScreenshotManager(), Add);
|
||||||
|
|
||||||
//overlay elements
|
//overlay elements
|
||||||
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
|
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
|
||||||
@ -302,6 +302,21 @@ namespace osu.Game
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
|
||||||
|
foreach (var overlay in singleDisplaySideOverlays)
|
||||||
|
{
|
||||||
|
overlay.StateChanged += state =>
|
||||||
|
{
|
||||||
|
if (state == Visibility.Hidden) return;
|
||||||
|
|
||||||
|
foreach (var c in singleDisplaySideOverlays)
|
||||||
|
{
|
||||||
|
if (c == overlay) continue;
|
||||||
|
c.State = Visibility.Hidden;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time.
|
// eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time.
|
||||||
var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile };
|
var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile };
|
||||||
foreach (var overlay in informationalOverlays)
|
foreach (var overlay in informationalOverlays)
|
||||||
|
@ -105,6 +105,7 @@ namespace osu.Game
|
|||||||
runMigrations();
|
runMigrations();
|
||||||
|
|
||||||
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
|
dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio));
|
||||||
|
dependencies.CacheAs<ISkinSource>(SkinManager);
|
||||||
|
|
||||||
var api = new APIAccess(LocalConfig);
|
var api = new APIAccess(LocalConfig);
|
||||||
|
|
||||||
@ -113,7 +114,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
|
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
|
||||||
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
|
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
|
||||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Host));
|
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host));
|
||||||
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore));
|
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore));
|
||||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore));
|
||||||
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
dependencies.Cache(SettingsStore = new SettingsStore(contextFactory));
|
||||||
|
@ -110,17 +110,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private int runningDepth;
|
private int runningDepth;
|
||||||
|
|
||||||
private void notificationClosed()
|
private void notificationClosed() => updateCounts();
|
||||||
{
|
|
||||||
Schedule(() =>
|
|
||||||
{
|
|
||||||
// hide ourselves if all notifications have been dismissed.
|
|
||||||
if (totalCount == 0)
|
|
||||||
State = Visibility.Hidden;
|
|
||||||
});
|
|
||||||
|
|
||||||
updateCounts();
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Scheduler postScheduler = new Scheduler();
|
private readonly Scheduler postScheduler = new Scheduler();
|
||||||
|
|
||||||
@ -141,6 +131,8 @@ namespace osu.Game.Overlays
|
|||||||
var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)));
|
var section = sections.Children.FirstOrDefault(s => s.AcceptTypes.Any(accept => accept.IsAssignableFrom(ourType)));
|
||||||
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
|
section?.Add(notification, notification.DisplayOnTop ? -runningDepth : runningDepth);
|
||||||
|
|
||||||
|
State = Visibility.Visible;
|
||||||
|
|
||||||
updateCounts();
|
updateCounts();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings.Sections.Graphics
|
namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||||
@ -12,7 +14,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
Children = new[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
@ -24,6 +26,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
LabelText = "Rotate cursor when dragging",
|
LabelText = "Rotate cursor when dragging",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.CursorRotation)
|
Bindable = config.GetBindable<bool>(OsuSetting.CursorRotation)
|
||||||
},
|
},
|
||||||
|
new SettingsEnumDropdown<ScreenshotFormat>
|
||||||
|
{
|
||||||
|
LabelText = "Screenshot format",
|
||||||
|
Bindable = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.Name, s.ID));
|
void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair<string, int>(s.ToString(), s.ID));
|
||||||
skins.ItemAdded += _ => reloadSkins();
|
skins.ItemAdded += _ => reloadSkins();
|
||||||
skins.ItemRemoved += _ => reloadSkins();
|
skins.ItemRemoved += _ => reloadSkins();
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -16,6 +17,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Edit.Tools;
|
using osu.Game.Rulesets.Edit.Tools;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Edit.Screens.Compose;
|
||||||
using osu.Game.Screens.Edit.Screens.Compose.Layers;
|
using osu.Game.Screens.Edit.Screens.Compose.Layers;
|
||||||
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
|
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
private readonly List<Container> layerContainers = new List<Container>();
|
private readonly List<Container> layerContainers = new List<Container>();
|
||||||
|
|
||||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||||
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
|
||||||
private IAdjustableClock adjustableClock;
|
private IAdjustableClock adjustableClock;
|
||||||
|
|
||||||
@ -41,11 +44,14 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuGameBase osuGame, IAdjustableClock adjustableClock, IFrameBasedClock framedClock)
|
private void load([NotNull] OsuGameBase osuGame, [NotNull] IAdjustableClock adjustableClock, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor)
|
||||||
{
|
{
|
||||||
this.adjustableClock = adjustableClock;
|
this.adjustableClock = adjustableClock;
|
||||||
|
|
||||||
|
if (beatDivisor != null)
|
||||||
|
this.beatDivisor.BindTo(beatDivisor);
|
||||||
|
|
||||||
beatmap.BindTo(osuGame.Beatmap);
|
beatmap.BindTo(osuGame.Beatmap);
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -167,9 +173,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
private void seek(int direction, bool snapped)
|
private void seek(int direction, bool snapped)
|
||||||
{
|
{
|
||||||
// Todo: This should not be a constant, but feels good for now
|
|
||||||
const int beat_snap_divisor = 4;
|
|
||||||
|
|
||||||
var cpi = beatmap.Value.Beatmap.ControlPointInfo;
|
var cpi = beatmap.Value.Beatmap.ControlPointInfo;
|
||||||
|
|
||||||
var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime);
|
var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime);
|
||||||
@ -181,7 +184,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
timingPoint = cpi.TimingPoints[--activeIndex];
|
timingPoint = cpi.TimingPoints[--activeIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
double seekAmount = timingPoint.BeatLength / beat_snap_divisor;
|
double seekAmount = timingPoint.BeatLength / beatDivisor;
|
||||||
double seekTime = adjustableClock.CurrentTime + seekAmount * direction;
|
double seekTime = adjustableClock.CurrentTime + seekAmount * direction;
|
||||||
|
|
||||||
if (!snapped || cpi.TimingPoints.Count == 0)
|
if (!snapped || cpi.TimingPoints.Count == 0)
|
||||||
@ -222,9 +225,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
public void SeekTo(double seekTime, bool snapped = false)
|
public void SeekTo(double seekTime, bool snapped = false)
|
||||||
{
|
{
|
||||||
// Todo: This should not be a constant, but feels good for now
|
|
||||||
const int beat_snap_divisor = 4;
|
|
||||||
|
|
||||||
if (!snapped)
|
if (!snapped)
|
||||||
{
|
{
|
||||||
adjustableClock.Seek(seekTime);
|
adjustableClock.Seek(seekTime);
|
||||||
@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
}
|
}
|
||||||
|
|
||||||
var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime);
|
var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime);
|
||||||
double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor;
|
double beatSnapLength = timingPoint.BeatLength / beatDivisor;
|
||||||
|
|
||||||
// We will be snapping to beats within the timing point
|
// We will be snapping to beats within the timing point
|
||||||
seekTime -= timingPoint.Time;
|
seekTime -= timingPoint.Time;
|
||||||
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -19,7 +18,7 @@ using OpenTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Drawables
|
namespace osu.Game.Rulesets.Objects.Drawables
|
||||||
{
|
{
|
||||||
public abstract class DrawableHitObject : CompositeDrawable, IHasAccentColour
|
public abstract class DrawableHitObject : SkinReloadableDrawable, IHasAccentColour
|
||||||
{
|
{
|
||||||
public readonly HitObject HitObject;
|
public readonly HitObject HitObject;
|
||||||
|
|
||||||
|
26
osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs
Normal file
26
osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects.Types
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A HitObject that is part of a combo and has extended information about its position relative to other combo objects.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasComboIndex : IHasCombo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The offset of this hitobject in the current combo.
|
||||||
|
/// </summary>
|
||||||
|
int IndexInCurrentCombo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The offset of this hitobject in the current combo.
|
||||||
|
/// </summary>
|
||||||
|
int ComboIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this is the last object in the current combo.
|
||||||
|
/// </summary>
|
||||||
|
bool LastInCombo { get; set; }
|
||||||
|
}
|
||||||
|
}
|
26
osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs
Normal file
26
osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects.Types
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A HitObject that is part of a combo and has extended information about its position relative to other combo objects.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasComboInformation : IHasCombo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The offset of this hitobject in the current combo.
|
||||||
|
/// </summary>
|
||||||
|
int IndexInCurrentCombo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The offset of this combo in relation to the beatmap.
|
||||||
|
/// </summary>
|
||||||
|
int ComboIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this is the last object in the current combo.
|
||||||
|
/// </summary>
|
||||||
|
bool LastInCombo { get; set; }
|
||||||
|
}
|
||||||
|
}
|
398
osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs
Normal file
398
osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
using OpenTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Screens.Compose
|
||||||
|
{
|
||||||
|
public class BeatDivisorControl : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
private int currentDivisorIndex;
|
||||||
|
|
||||||
|
public BeatDivisorControl(BindableBeatDivisor beatDivisor)
|
||||||
|
{
|
||||||
|
this.beatDivisor.BindTo(beatDivisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Name = "Gray Background",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colours.Gray4
|
||||||
|
},
|
||||||
|
new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Name = "Black Background",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black
|
||||||
|
},
|
||||||
|
new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colours.Gray4
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Horizontal = 5 },
|
||||||
|
Child = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new DivisorButton
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.fa_chevron_left,
|
||||||
|
Action = beatDivisor.Previous
|
||||||
|
},
|
||||||
|
new DivisorText(beatDivisor),
|
||||||
|
new DivisorButton
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.fa_chevron_right,
|
||||||
|
Action = beatDivisor.Next
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.Absolute, 20),
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new TextFlowContainer(s => s.TextSize = 14)
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding { Horizontal = 15 },
|
||||||
|
Text = "beat snap divisor",
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
TextAnchor = Anchor.TopCentre
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.Absolute, 30),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 25),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DivisorText : SpriteText
|
||||||
|
{
|
||||||
|
private readonly Bindable<int> beatDivisor = new Bindable<int>();
|
||||||
|
|
||||||
|
public DivisorText(BindableBeatDivisor beatDivisor)
|
||||||
|
{
|
||||||
|
this.beatDivisor.BindTo(beatDivisor);
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Colour = colours.BlueLighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
beatDivisor.ValueChanged += v => updateText();
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateText() => Text = $"1/{beatDivisor.Value}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DivisorButton : IconButton
|
||||||
|
{
|
||||||
|
public DivisorButton()
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
// Small offset to look a bit better centered along with the divisor text
|
||||||
|
Y = 1;
|
||||||
|
|
||||||
|
ButtonSize = new Vector2(20);
|
||||||
|
IconScale = new Vector2(0.6f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
IconColour = Color4.Black;
|
||||||
|
HoverColour = colours.Gray7;
|
||||||
|
FlashColour = colours.Gray9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TickSliderBar : SliderBar<int>
|
||||||
|
{
|
||||||
|
private Marker marker;
|
||||||
|
|
||||||
|
private readonly BindableBeatDivisor beatDivisor;
|
||||||
|
private readonly int[] availableDivisors;
|
||||||
|
|
||||||
|
public TickSliderBar(BindableBeatDivisor beatDivisor, params int[] divisors)
|
||||||
|
{
|
||||||
|
CurrentNumber.BindTo(this.beatDivisor = beatDivisor);
|
||||||
|
availableDivisors = divisors;
|
||||||
|
|
||||||
|
Padding = new MarginPadding { Horizontal = 5 };
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
foreach (var t in availableDivisors)
|
||||||
|
{
|
||||||
|
AddInternal(new Tick(t)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativePositionAxes = Axes.X,
|
||||||
|
X = getMappedPosition(t)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AddInternal(marker = new Marker());
|
||||||
|
|
||||||
|
CurrentNumber.ValueChanged += v =>
|
||||||
|
{
|
||||||
|
marker.MoveToX(getMappedPosition(v), 100, Easing.OutQuint);
|
||||||
|
marker.Flash();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateValue(float value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HandleKeyboardInput => IsHovered && !CurrentNumber.Disabled;
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
|
{
|
||||||
|
switch (args.Key)
|
||||||
|
{
|
||||||
|
case Key.Right:
|
||||||
|
beatDivisor.Next();
|
||||||
|
OnUserChange();
|
||||||
|
return true;
|
||||||
|
case Key.Left:
|
||||||
|
beatDivisor.Previous();
|
||||||
|
OnUserChange();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||||
|
{
|
||||||
|
marker.Active = true;
|
||||||
|
return base.OnMouseDown(state, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||||
|
{
|
||||||
|
marker.Active = false;
|
||||||
|
return base.OnMouseUp(state, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(InputState state)
|
||||||
|
{
|
||||||
|
handleMouseInput(state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnDrag(InputState state)
|
||||||
|
{
|
||||||
|
handleMouseInput(state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleMouseInput(InputState state)
|
||||||
|
{
|
||||||
|
// copied from SliderBar so we can do custom spacing logic.
|
||||||
|
var xPosition = (ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding) / UsableWidth;
|
||||||
|
|
||||||
|
CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First();
|
||||||
|
OnUserChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f);
|
||||||
|
|
||||||
|
private class Tick : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly int divisor;
|
||||||
|
|
||||||
|
public Tick(int divisor)
|
||||||
|
{
|
||||||
|
this.divisor = divisor;
|
||||||
|
Size = new Vector2(2.5f, 10);
|
||||||
|
|
||||||
|
InternalChild = new Box { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
CornerRadius = 0.5f;
|
||||||
|
Masking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Colour = getColourForDivisor(divisor, colours);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ColourInfo getColourForDivisor(int divisor, OsuColour colours)
|
||||||
|
{
|
||||||
|
switch (divisor)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
return colours.BlueLight;
|
||||||
|
case 4:
|
||||||
|
return colours.Blue;
|
||||||
|
case 8:
|
||||||
|
return colours.BlueDarker;
|
||||||
|
case 16:
|
||||||
|
return colours.PurpleDark;
|
||||||
|
case 3:
|
||||||
|
return colours.YellowLight;
|
||||||
|
case 6:
|
||||||
|
return colours.Yellow;
|
||||||
|
case 12:
|
||||||
|
return colours.YellowDarker;
|
||||||
|
default:
|
||||||
|
return Color4.White;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Marker : CompositeDrawable
|
||||||
|
{
|
||||||
|
private Color4 defaultColour;
|
||||||
|
|
||||||
|
private const float size = 7;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Colour = defaultColour = colours.Gray4;
|
||||||
|
Anchor = Anchor.TopLeft;
|
||||||
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
|
Width = size;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
RelativePositionAxes = Axes.X;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Width = 2,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White),
|
||||||
|
Blending = BlendingMode.Additive,
|
||||||
|
},
|
||||||
|
new EquilateralTriangle
|
||||||
|
{
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Height = size,
|
||||||
|
EdgeSmoothness = new Vector2(1),
|
||||||
|
Colour = Color4.White,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool active;
|
||||||
|
|
||||||
|
public bool Active
|
||||||
|
{
|
||||||
|
get => active;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.FadeColour(value ? Color4.White : defaultColour, 500, Easing.OutQuint);
|
||||||
|
active = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flash()
|
||||||
|
{
|
||||||
|
bool wasActive = active;
|
||||||
|
|
||||||
|
Active = true;
|
||||||
|
|
||||||
|
if (wasActive) return;
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(50))
|
||||||
|
Active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs
Normal file
39
osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Screens.Compose
|
||||||
|
{
|
||||||
|
public class BindableBeatDivisor : BindableNumber<int>
|
||||||
|
{
|
||||||
|
public static readonly int[] VALID_DIVISORS = { 1, 2, 3, 4, 6, 8, 12, 16 };
|
||||||
|
|
||||||
|
public BindableBeatDivisor(int value = 1)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Next() => Value = VALID_DIVISORS[Math.Min(VALID_DIVISORS.Length - 1, Array.IndexOf(VALID_DIVISORS, Value) + 1)];
|
||||||
|
|
||||||
|
public void Previous() => Value = VALID_DIVISORS[Math.Max(0, Array.IndexOf(VALID_DIVISORS, Value) - 1)];
|
||||||
|
|
||||||
|
public override int Value
|
||||||
|
{
|
||||||
|
get { return base.Value; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!VALID_DIVISORS.Contains(value))
|
||||||
|
throw new ArgumentOutOfRangeException($"Provided divisor is not in {nameof(VALID_DIVISORS)}");
|
||||||
|
|
||||||
|
base.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override int DefaultMinValue => VALID_DIVISORS.First();
|
||||||
|
protected override int DefaultMaxValue => VALID_DIVISORS.Last();
|
||||||
|
protected override int DefaultPrecision => 1;
|
||||||
|
}
|
||||||
|
}
|
@ -17,11 +17,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose
|
|||||||
private const float vertical_margins = 10;
|
private const float vertical_margins = 10;
|
||||||
private const float horizontal_margins = 20;
|
private const float horizontal_margins = 20;
|
||||||
|
|
||||||
|
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
|
||||||
|
|
||||||
private Container composerContainer;
|
private Container composerContainer;
|
||||||
|
|
||||||
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
=> dependencies = new DependencyContainer(parent);
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
dependencies.Cache(beatDivisor);
|
||||||
|
|
||||||
ScrollableTimeline timeline;
|
ScrollableTimeline timeline;
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -48,15 +57,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose
|
|||||||
Name = "Timeline content",
|
Name = "Timeline content",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
|
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
|
||||||
Children = new Drawable[]
|
Child = new GridContainer
|
||||||
{
|
{
|
||||||
new Container
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Content = new[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new Drawable[]
|
||||||
Padding = new MarginPadding { Right = 115 },
|
{
|
||||||
Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both }
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Right = 5 },
|
||||||
|
Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both }
|
||||||
|
},
|
||||||
|
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ColumnDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 90),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
rectangle,
|
rectangle,
|
||||||
colourInfo,
|
colourInfo,
|
||||||
null,
|
null,
|
||||||
Shared.VertexBatch.Add,
|
Shared.VertexBatch.AddAction,
|
||||||
//barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that.
|
//barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that.
|
||||||
Vector2.Divide(inflation, barSize.Yx));
|
Vector2.Divide(inflation, barSize.Yx));
|
||||||
}
|
}
|
||||||
|
@ -337,12 +337,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool interactive => Action != null && Alpha > 0.2f;
|
public override bool HandleMouseInput => base.HandleMouseInput && Action != null && Alpha > 0.2f;
|
||||||
|
|
||||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||||
{
|
{
|
||||||
if (!interactive) return false;
|
|
||||||
|
|
||||||
logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out);
|
logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -355,8 +353,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
protected override bool OnClick(InputState state)
|
protected override bool OnClick(InputState state)
|
||||||
{
|
{
|
||||||
if (!interactive) return false;
|
|
||||||
|
|
||||||
if (Action?.Invoke() ?? true)
|
if (Action?.Invoke() ?? true)
|
||||||
sampleClick.Play();
|
sampleClick.Play();
|
||||||
|
|
||||||
@ -368,8 +364,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
protected override bool OnHover(InputState state)
|
||||||
{
|
{
|
||||||
if (!interactive) return false;
|
|
||||||
|
|
||||||
logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic);
|
logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Storyboards.Drawables;
|
using osu.Game.Storyboards.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
@ -163,7 +164,11 @@ namespace osu.Game.Screens.Play
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
RulesetContainer,
|
new LocalSkinOverrideContainer(working.Skin)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = RulesetContainer
|
||||||
|
},
|
||||||
new SkipOverlay(firstObjectTime)
|
new SkipOverlay(firstObjectTime)
|
||||||
{
|
{
|
||||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
||||||
|
@ -139,8 +139,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
|
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
|
||||||
// if we become unready for push during the delay.
|
// if we become unready for push during the delay.
|
||||||
pushDebounce?.Cancel();
|
cancelLoad();
|
||||||
pushDebounce = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,10 +171,23 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cancelLoad()
|
||||||
|
{
|
||||||
|
pushDebounce?.Cancel();
|
||||||
|
pushDebounce = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnSuspending(Screen next)
|
||||||
|
{
|
||||||
|
base.OnSuspending(next);
|
||||||
|
cancelLoad();
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnExiting(Screen next)
|
protected override bool OnExiting(Screen next)
|
||||||
{
|
{
|
||||||
Content.ScaleTo(0.7f, 150, Easing.InQuint);
|
Content.ScaleTo(0.7f, 150, Easing.InQuint);
|
||||||
this.FadeOut(150);
|
this.FadeOut(150);
|
||||||
|
cancelLoad();
|
||||||
|
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
|
@ -259,6 +259,8 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private void workingBeatmapChanged(WorkingBeatmap beatmap)
|
private void workingBeatmapChanged(WorkingBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
if (beatmap is DummyWorkingBeatmap) return;
|
||||||
|
|
||||||
if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false))
|
if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false))
|
||||||
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
|
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
|
||||||
if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value)
|
if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value)
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
@ -11,16 +13,22 @@ namespace osu.Game.Skinning
|
|||||||
public DefaultSkin()
|
public DefaultSkin()
|
||||||
: base(SkinInfo.Default)
|
: base(SkinInfo.Default)
|
||||||
{
|
{
|
||||||
|
Configuration = new SkinConfiguration
|
||||||
|
{
|
||||||
|
ComboColours =
|
||||||
|
{
|
||||||
|
new Color4(17, 136, 170, 255),
|
||||||
|
new Color4(102, 136, 0, 255),
|
||||||
|
new Color4(204, 102, 0, 255),
|
||||||
|
new Color4(121, 9, 13, 255)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable GetDrawableComponent(string componentName)
|
public override Drawable GetDrawableComponent(string componentName) => null;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override SampleChannel GetSample(string sampleName)
|
public override Texture GetTexture(string componentName) => null;
|
||||||
{
|
|
||||||
return null;
|
public override SampleChannel GetSample(string sampleName) => null;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
osu.Game/Skinning/ISkinSource.cs
Normal file
28
osu.Game/Skinning/ISkinSource.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 System;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to skinnable elements.
|
||||||
|
/// </summary>
|
||||||
|
public interface ISkinSource
|
||||||
|
{
|
||||||
|
event Action SourceChanged;
|
||||||
|
|
||||||
|
Drawable GetDrawableComponent(string componentName);
|
||||||
|
|
||||||
|
Texture GetTexture(string componentName);
|
||||||
|
|
||||||
|
SampleChannel GetSample(string sampleName);
|
||||||
|
|
||||||
|
TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class;
|
||||||
|
|
||||||
|
TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct;
|
||||||
|
}
|
||||||
|
}
|
20
osu.Game/Skinning/LegacyBeatmapSkin.cs
Normal file
20
osu.Game/Skinning/LegacyBeatmapSkin.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.IO.Stores;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyBeatmapSkin : LegacySkin
|
||||||
|
{
|
||||||
|
public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||||
|
: base(createSkinInfo(beatmap), new LegacySkinResourceStore<BeatmapSetFileInfo>(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>
|
||||||
|
new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() };
|
||||||
|
}
|
||||||
|
}
|
@ -10,21 +10,33 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
|
using osu.Game.Database;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public class LegacySkin : Skin
|
public class LegacySkin : Skin
|
||||||
{
|
{
|
||||||
private readonly TextureStore textures;
|
protected TextureStore Textures;
|
||||||
|
|
||||||
private readonly SampleManager samples;
|
protected SampleManager Samples;
|
||||||
|
|
||||||
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||||
: base(skin)
|
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
|
||||||
{
|
{
|
||||||
storage = new LegacySkinResourceStore(skin, storage);
|
}
|
||||||
samples = audioManager.GetSampleManager(storage);
|
|
||||||
textures = new TextureStore(new RawTextureLoaderStore(storage));
|
protected LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, string filename) : base(skin)
|
||||||
|
{
|
||||||
|
Stream stream = storage.GetStream(filename);
|
||||||
|
if (stream != null)
|
||||||
|
using (StreamReader reader = new StreamReader(stream))
|
||||||
|
Configuration = new LegacySkinDecoder().Decode(reader);
|
||||||
|
else
|
||||||
|
Configuration = new SkinConfiguration();
|
||||||
|
|
||||||
|
Samples = audioManager.GetSampleManager(storage);
|
||||||
|
Textures = new TextureStore(new RawTextureLoaderStore(storage));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable GetDrawableComponent(string componentName)
|
public override Drawable GetDrawableComponent(string componentName)
|
||||||
@ -45,37 +57,62 @@ namespace osu.Game.Skinning
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var texture = textures.Get(componentName);
|
float ratio = 0.72f; // brings sizing roughly in-line with stable
|
||||||
|
|
||||||
|
var texture = GetTexture($"{componentName}@2x");
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
ratio *= 2;
|
||||||
|
GetTexture(componentName);
|
||||||
|
}
|
||||||
|
|
||||||
if (texture == null) return null;
|
if (texture == null) return null;
|
||||||
|
|
||||||
return new Sprite { Texture = texture };
|
return new Sprite
|
||||||
|
{
|
||||||
|
Texture = texture,
|
||||||
|
Scale = new Vector2(ratio),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SampleChannel GetSample(string sampleName) => samples.Get(sampleName);
|
public override Texture GetTexture(string componentName) => Textures.Get(componentName);
|
||||||
|
|
||||||
private class LegacySkinResourceStore : IResourceStore<byte[]>
|
public override SampleChannel GetSample(string sampleName) => Samples.Get(sampleName);
|
||||||
|
|
||||||
|
protected class LegacySkinResourceStore<T> : IResourceStore<byte[]>
|
||||||
|
where T : INamedFileInfo
|
||||||
{
|
{
|
||||||
private readonly SkinInfo skin;
|
private readonly IHasFiles<T> source;
|
||||||
private readonly IResourceStore<byte[]> underlyingStore;
|
private readonly IResourceStore<byte[]> underlyingStore;
|
||||||
|
|
||||||
private string getPathForFile(string filename)
|
private string getPathForFile(string filename)
|
||||||
{
|
{
|
||||||
|
bool hasExtension = filename.Contains('.');
|
||||||
|
|
||||||
string lastPiece = filename.Split('/').Last();
|
string lastPiece = filename.Split('/').Last();
|
||||||
|
|
||||||
var file = skin.Files.FirstOrDefault(f =>
|
var file = source.Files.FirstOrDefault(f =>
|
||||||
string.Equals(Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase));
|
string.Equals(hasExtension ? f.Filename : Path.GetFileNameWithoutExtension(f.Filename), lastPiece, StringComparison.InvariantCultureIgnoreCase));
|
||||||
return file?.FileInfo.StoragePath;
|
return file?.FileInfo.StoragePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LegacySkinResourceStore(SkinInfo skin, IResourceStore<byte[]> underlyingStore)
|
public LegacySkinResourceStore(IHasFiles<T> source, IResourceStore<byte[]> underlyingStore)
|
||||||
{
|
{
|
||||||
this.skin = skin;
|
this.source = source;
|
||||||
this.underlyingStore = underlyingStore;
|
this.underlyingStore = underlyingStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream GetStream(string name) => underlyingStore.GetStream(getPathForFile(name));
|
public Stream GetStream(string name)
|
||||||
|
{
|
||||||
|
string path = getPathForFile(name);
|
||||||
|
return path == null ? null : underlyingStore.GetStream(path);
|
||||||
|
}
|
||||||
|
|
||||||
byte[] IResourceStore<byte[]>.Get(string name) => underlyingStore.Get(getPathForFile(name));
|
byte[] IResourceStore<byte[]>.Get(string name)
|
||||||
|
{
|
||||||
|
string path = getPathForFile(name);
|
||||||
|
return path == null ? null : underlyingStore.Get(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
osu.Game/Skinning/LegacySkinDecoder.cs
Normal file
38
osu.Game/Skinning/LegacySkinDecoder.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class LegacySkinDecoder : LegacyDecoder<SkinConfiguration>
|
||||||
|
{
|
||||||
|
public LegacySkinDecoder()
|
||||||
|
: base(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ParseLine(SkinConfiguration output, Section section, string line)
|
||||||
|
{
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case Section.General:
|
||||||
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
|
switch (pair.Key)
|
||||||
|
{
|
||||||
|
case @"Name":
|
||||||
|
output.SkinInfo.Name = pair.Value;
|
||||||
|
break;
|
||||||
|
case @"Author":
|
||||||
|
output.SkinInfo.Creator = pair.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.ParseLine(output, section, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
osu.Game/Skinning/LocalSkinOverrideContainer.cs
Normal file
72
osu.Game/Skinning/LocalSkinOverrideContainer.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class LocalSkinOverrideContainer : Container, ISkinSource
|
||||||
|
{
|
||||||
|
public event Action SourceChanged;
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(string componentName) => source.GetDrawableComponent(componentName) ?? fallbackSource?.GetDrawableComponent(componentName);
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName) => source.GetTexture(componentName) ?? fallbackSource.GetTexture(componentName);
|
||||||
|
|
||||||
|
public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName);
|
||||||
|
|
||||||
|
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct
|
||||||
|
{
|
||||||
|
TValue? val = null;
|
||||||
|
var conf = (source as Skin)?.Configuration as TConfiguration;
|
||||||
|
if (conf != null)
|
||||||
|
val = query?.Invoke(conf);
|
||||||
|
|
||||||
|
return val ?? fallbackSource?.GetValue(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class
|
||||||
|
{
|
||||||
|
TValue val = null;
|
||||||
|
var conf = (source as Skin)?.Configuration as TConfiguration;
|
||||||
|
if (conf != null)
|
||||||
|
val = query?.Invoke(conf);
|
||||||
|
|
||||||
|
return val ?? fallbackSource?.GetValue(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ISkinSource source;
|
||||||
|
private ISkinSource fallbackSource;
|
||||||
|
|
||||||
|
public LocalSkinOverrideContainer(ISkinSource source)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||||
|
|
||||||
|
fallbackSource = dependencies.Get<ISkinSource>();
|
||||||
|
if (fallbackSource != null)
|
||||||
|
fallbackSource.SourceChanged += () => SourceChanged?.Invoke();
|
||||||
|
|
||||||
|
dependencies.CacheAs<ISkinSource>(this);
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (fallbackSource != null)
|
||||||
|
fallbackSource.SourceChanged -= SourceChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +1,60 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public abstract class Skin
|
public abstract class Skin : IDisposable, ISkinSource
|
||||||
{
|
{
|
||||||
public readonly SkinInfo SkinInfo;
|
public readonly SkinInfo SkinInfo;
|
||||||
|
|
||||||
|
public virtual SkinConfiguration Configuration { get; protected set; }
|
||||||
|
|
||||||
|
public event Action SourceChanged;
|
||||||
|
|
||||||
public abstract Drawable GetDrawableComponent(string componentName);
|
public abstract Drawable GetDrawableComponent(string componentName);
|
||||||
|
|
||||||
public abstract SampleChannel GetSample(string sampleName);
|
public abstract SampleChannel GetSample(string sampleName);
|
||||||
|
|
||||||
|
public abstract Texture GetTexture(string componentName);
|
||||||
|
|
||||||
|
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class
|
||||||
|
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
|
||||||
|
|
||||||
|
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct
|
||||||
|
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
|
||||||
|
|
||||||
protected Skin(SkinInfo skin)
|
protected Skin(SkinInfo skin)
|
||||||
{
|
{
|
||||||
SkinInfo = skin;
|
SkinInfo = skin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Disposal
|
||||||
|
|
||||||
|
~Skin()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isDisposed;
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
if (isDisposed)
|
||||||
|
return;
|
||||||
|
isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
osu.Game/Skinning/SkinConfiguration.cs
Normal file
18
osu.Game/Skinning/SkinConfiguration.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class SkinConfiguration : IHasComboColours, IHasCustomColours
|
||||||
|
{
|
||||||
|
public readonly SkinInfo SkinInfo = new SkinInfo();
|
||||||
|
|
||||||
|
public List<Color4> ComboColours { get; set; } = new List<Color4>();
|
||||||
|
|
||||||
|
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
|
||||||
|
}
|
||||||
|
}
|
@ -24,5 +24,7 @@ namespace osu.Game.Skinning
|
|||||||
public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" };
|
public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" };
|
||||||
|
|
||||||
public bool Equals(SkinInfo other) => other != null && ID == other.ID;
|
public bool Equals(SkinInfo other) => other != null && ID == other.ID;
|
||||||
|
|
||||||
|
public override string ToString() => $"\"{Name}\" by {Creator}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,17 @@ using System.Linq;
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.IO.Archives;
|
using osu.Game.IO.Archives;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public class SkinManager : ArchiveModelManager<SkinInfo, SkinFileInfo>
|
public class SkinManager : ArchiveModelManager<SkinInfo, SkinFileInfo>, ISkinSource
|
||||||
{
|
{
|
||||||
private readonly AudioManager audio;
|
private readonly AudioManager audio;
|
||||||
|
|
||||||
@ -39,6 +42,31 @@ namespace osu.Game.Skinning
|
|||||||
Name = archive.Name
|
Name = archive.Name
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected override void Populate(SkinInfo model, ArchiveReader archive)
|
||||||
|
{
|
||||||
|
base.Populate(model, archive);
|
||||||
|
populate(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populate a <see cref="SkinInfo"/> from its <see cref="SkinConfiguration"/> (if possible).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model"></param>
|
||||||
|
private void populate(SkinInfo model)
|
||||||
|
{
|
||||||
|
Skin reference = GetSkin(model);
|
||||||
|
if (!string.IsNullOrEmpty(reference.Configuration.SkinInfo.Name))
|
||||||
|
{
|
||||||
|
model.Name = reference.Configuration.SkinInfo.Name;
|
||||||
|
model.Creator = reference.Configuration.SkinInfo.Creator;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
model.Name = model.Name.Replace(".osk", "");
|
||||||
|
model.Creator = "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a <see cref="Skin"/> instance for the provided <see cref="SkinInfo"/>
|
/// Retrieve a <see cref="Skin"/> instance for the provided <see cref="SkinInfo"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -64,7 +92,19 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
if (skin.SkinInfo != CurrentSkinInfo.Value)
|
if (skin.SkinInfo != CurrentSkinInfo.Value)
|
||||||
throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead.");
|
throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead.");
|
||||||
|
|
||||||
|
SourceChanged?.Invoke();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// migrate older imports which didn't have access to skin.ini
|
||||||
|
using (ContextFactory.GetForWrite())
|
||||||
|
{
|
||||||
|
foreach (var skinInfo in ModelStore.ConsumableItems.Where(s => s.Name.EndsWith(".osk")))
|
||||||
|
{
|
||||||
|
populate(skinInfo);
|
||||||
|
Update(skinInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -73,5 +113,17 @@ namespace osu.Game.Skinning
|
|||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
/// <returns>The first result for the provided query, or null if no results were found.</returns>
|
||||||
public SkinInfo Query(Expression<Func<SkinInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query);
|
public SkinInfo Query(Expression<Func<SkinInfo, bool>> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query);
|
||||||
|
|
||||||
|
public event Action SourceChanged;
|
||||||
|
|
||||||
|
public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName);
|
||||||
|
|
||||||
|
public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName);
|
||||||
|
|
||||||
|
public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName);
|
||||||
|
|
||||||
|
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetValue(query);
|
||||||
|
|
||||||
|
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetValue(query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
@ -12,33 +12,36 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SkinReloadableDrawable : CompositeDrawable
|
public abstract class SkinReloadableDrawable : CompositeDrawable
|
||||||
{
|
{
|
||||||
private Bindable<Skin> skin;
|
private readonly Func<ISkinSource, bool> allowFallback;
|
||||||
|
private ISkinSource skin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
|
/// Whether fallback to default skin should be allowed if the custom skin is missing this resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly bool allowDefaultFallback;
|
private bool allowDefaultFallback => allowFallback == null || allowFallback.Invoke(skin);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new <see cref="SkinReloadableDrawable"/>
|
/// Create a new <see cref="SkinReloadableDrawable"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
|
/// <param name="fallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
|
||||||
protected SkinReloadableDrawable(bool fallback = true)
|
protected SkinReloadableDrawable(Func<ISkinSource, bool> allowFallback = null)
|
||||||
{
|
{
|
||||||
allowDefaultFallback = fallback;
|
this.allowFallback = allowFallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(SkinManager skinManager)
|
private void load(ISkinSource source)
|
||||||
{
|
{
|
||||||
skin = skinManager.CurrentSkin.GetBoundCopy();
|
skin = source;
|
||||||
skin.ValueChanged += skin => SkinChanged(skin, allowDefaultFallback || skin.SkinInfo == SkinInfo.Default);
|
skin.SourceChanged += onChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onChange() => SkinChanged(skin, allowDefaultFallback);
|
||||||
|
|
||||||
protected override void LoadAsyncComplete()
|
protected override void LoadAsyncComplete()
|
||||||
{
|
{
|
||||||
base.LoadAsyncComplete();
|
base.LoadAsyncComplete();
|
||||||
skin.TriggerChange();
|
onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -46,7 +49,7 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="skin">The new skin.</param>
|
/// <param name="skin">The new skin.</param>
|
||||||
/// <param name="allowFallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
|
/// <param name="allowFallback">Whether fallback to default skin should be allowed if the custom skin is missing this resource.</param>
|
||||||
protected virtual void SkinChanged(Skin skin, bool allowFallback)
|
protected virtual void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public class SkinnableDrawable : SkinnableDrawable<Drawable>
|
public class SkinnableDrawable : SkinnableDrawable<Drawable>
|
||||||
{
|
{
|
||||||
public SkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, bool fallback = true, bool restrictSize = true)
|
public SkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
|
||||||
: base(name, defaultImplementation, fallback, restrictSize)
|
: base(name, defaultImplementation, allowFallback, restrictSize)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
|
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
|
||||||
/// <param name="fallback">Whther to fallback to the default implementation when a custom skin is specified but not implementation is present.</param>
|
/// <param name="fallback">Whther to fallback to the default implementation when a custom skin is specified but not implementation is present.</param>
|
||||||
/// <param name="restrictSize">Whether a user-skin drawable should be limited to the size of our parent.</param>
|
/// <param name="restrictSize">Whether a user-skin drawable should be limited to the size of our parent.</param>
|
||||||
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, bool fallback = true, bool restrictSize = true) : base(fallback)
|
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true) : base(allowFallback)
|
||||||
{
|
{
|
||||||
componentName = name;
|
componentName = name;
|
||||||
createDefault = defaultImplementation;
|
createDefault = defaultImplementation;
|
||||||
@ -40,7 +40,7 @@ namespace osu.Game.Skinning
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(Skin skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
{
|
{
|
||||||
var drawable = skin.GetDrawableComponent(componentName);
|
var drawable = skin.GetDrawableComponent(componentName);
|
||||||
if (drawable != null)
|
if (drawable != null)
|
||||||
@ -49,6 +49,7 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
drawable.RelativeSizeAxes = Axes.Both;
|
drawable.RelativeSizeAxes = Axes.Both;
|
||||||
drawable.Size = Vector2.One;
|
drawable.Size = Vector2.One;
|
||||||
|
drawable.Scale = Vector2.One;
|
||||||
drawable.FillMode = FillMode.Fit;
|
drawable.FillMode = FillMode.Fit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
public void Play() => channels?.ForEach(c => c.Play());
|
public void Play() => channels?.ForEach(c => c.Play());
|
||||||
|
|
||||||
protected override void SkinChanged(Skin skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
{
|
{
|
||||||
channels = samples.Select(s =>
|
channels = samples.Select(s =>
|
||||||
{
|
{
|
||||||
|
@ -187,6 +187,7 @@
|
|||||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.2.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.2.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||||
<HintPath>$(SolutionDir)\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\System.Diagnostics.DiagnosticSource.4.4.1\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Interactive.Async, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
|
<Reference Include="System.Interactive.Async, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
|
||||||
<HintPath>$(SolutionDir)\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll</HintPath>
|
<HintPath>$(SolutionDir)\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@ -290,6 +291,7 @@
|
|||||||
<Compile Include="Database\SingletonContextFactory.cs" />
|
<Compile Include="Database\SingletonContextFactory.cs" />
|
||||||
<Compile Include="Graphics\Containers\LinkFlowContainer.cs" />
|
<Compile Include="Graphics\Containers\LinkFlowContainer.cs" />
|
||||||
<Compile Include="Graphics\DrawableDate.cs" />
|
<Compile Include="Graphics\DrawableDate.cs" />
|
||||||
|
<Compile Include="Graphics\ScreenshotManager.cs" />
|
||||||
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
|
<Compile Include="Graphics\Textures\LargeTextureStore.cs" />
|
||||||
<Compile Include="IO\Archives\ArchiveReader.cs" />
|
<Compile Include="IO\Archives\ArchiveReader.cs" />
|
||||||
<Compile Include="IO\Archives\LegacyFilesystemReader.cs" />
|
<Compile Include="IO\Archives\LegacyFilesystemReader.cs" />
|
||||||
@ -374,12 +376,15 @@
|
|||||||
<Compile Include="Overlays\Social\SocialPanel.cs" />
|
<Compile Include="Overlays\Social\SocialPanel.cs" />
|
||||||
<Compile Include="Rulesets\Mods\IApplicableToDrawableHitObject.cs" />
|
<Compile Include="Rulesets\Mods\IApplicableToDrawableHitObject.cs" />
|
||||||
<Compile Include="Rulesets\Objects\HitWindows.cs" />
|
<Compile Include="Rulesets\Objects\HitWindows.cs" />
|
||||||
|
<Compile Include="Rulesets\Objects\Types\IHasComboInformation.cs" />
|
||||||
<Compile Include="Rulesets\Replays\Legacy\LegacyReplayFrame.cs" />
|
<Compile Include="Rulesets\Replays\Legacy\LegacyReplayFrame.cs" />
|
||||||
<Compile Include="Rulesets\Replays\Legacy\ReplayButtonState.cs" />
|
<Compile Include="Rulesets\Replays\Legacy\ReplayButtonState.cs" />
|
||||||
<Compile Include="Rulesets\Replays\ReplayFrame.cs" />
|
<Compile Include="Rulesets\Replays\ReplayFrame.cs" />
|
||||||
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
|
<Compile Include="Rulesets\Replays\Types\IConvertibleReplayFrame.cs" />
|
||||||
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
|
<Compile Include="Rulesets\Scoring\Legacy\LegacyScoreParser.cs" />
|
||||||
<Compile Include="Rulesets\UI\JudgementContainer.cs" />
|
<Compile Include="Rulesets\UI\JudgementContainer.cs" />
|
||||||
|
<Compile Include="Screens\Edit\Screens\Compose\BindableBeatDivisor.cs" />
|
||||||
|
<Compile Include="Screens\Edit\Screens\Compose\BeatDivisorControl.cs" />
|
||||||
<Compile Include="Screens\Edit\Screens\Compose\Layers\BorderLayer.cs" />
|
<Compile Include="Screens\Edit\Screens\Compose\Layers\BorderLayer.cs" />
|
||||||
<Compile Include="Screens\Edit\Screens\Compose\Layers\HitObjectMaskLayer.cs" />
|
<Compile Include="Screens\Edit\Screens\Compose\Layers\HitObjectMaskLayer.cs" />
|
||||||
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionBox.cs" />
|
<Compile Include="Screens\Edit\Screens\Compose\Layers\SelectionBox.cs" />
|
||||||
@ -871,9 +876,14 @@
|
|||||||
<Compile Include="Screens\Tournament\Teams\DrawingsTeam.cs" />
|
<Compile Include="Screens\Tournament\Teams\DrawingsTeam.cs" />
|
||||||
<Compile Include="Screens\Tournament\Teams\ITeamList.cs" />
|
<Compile Include="Screens\Tournament\Teams\ITeamList.cs" />
|
||||||
<Compile Include="Screens\Tournament\Teams\StorageBackedTeamList.cs" />
|
<Compile Include="Screens\Tournament\Teams\StorageBackedTeamList.cs" />
|
||||||
|
<Compile Include="Skinning\LegacyBeatmapSkin.cs" />
|
||||||
<Compile Include="Skinning\DefaultSkin.cs" />
|
<Compile Include="Skinning\DefaultSkin.cs" />
|
||||||
|
<Compile Include="Skinning\ISkinSource.cs" />
|
||||||
<Compile Include="Skinning\LegacySkin.cs" />
|
<Compile Include="Skinning\LegacySkin.cs" />
|
||||||
|
<Compile Include="Skinning\LegacySkinDecoder.cs" />
|
||||||
|
<Compile Include="Skinning\LocalSkinOverrideContainer.cs" />
|
||||||
<Compile Include="Skinning\Skin.cs" />
|
<Compile Include="Skinning\Skin.cs" />
|
||||||
|
<Compile Include="Skinning\SkinConfiguration.cs" />
|
||||||
<Compile Include="Skinning\SkinFileInfo.cs" />
|
<Compile Include="Skinning\SkinFileInfo.cs" />
|
||||||
<Compile Include="Skinning\SkinInfo.cs" />
|
<Compile Include="Skinning\SkinInfo.cs" />
|
||||||
<Compile Include="Skinning\SkinManager.cs" />
|
<Compile Include="Skinning\SkinManager.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user