mirror of
https://github.com/ppy/osu.git
synced 2026-05-19 05:09:54 +08:00
0988552567
I don't really have much to say here. Instead, I'll give a brief history rundown that lists many pages of documentation you can read, if interested. - Started off as BTMC + Happy24 (Vivi)'s ["The Vision"](https://docs.google.com/document/d/1p1IpPmd2RICp8G4OqkCSs7u8Ug8FbFv8qqP0mfSrHf0/edit?tab=t.0#heading=h.fol093d9f9xi) - Initial [designs](https://www.figma.com/design/f5qqC57t9jFlgpzhRqUNVX/The-Vision?node-id=0-1&p=f) were led by Vivi. - Designs [morphed](https://www.figma.com/design/vtFmLrXKvWNyYiRjTezFTM/Untitled--Copy-?node-id=0-1&p=f) during development into what's currently present, led by @minetoblend. - There is some more ongoing work creating a [game design document](https://docs.google.com/document/d/1iffJFCsIBfYF0D4ogItSBEj6YBmbp-rdCpItAeaJiTA/edit?tab=t.0). **tl;dr:** Create something with the mechanics of a trading card game within osu!. The name of this is "ranked play". --- To be frank, a lot of stuff is missing here. Some of it I don't want to mention, because the point of this exercise is to get the system into the hands of players, gather feedback especially around mechanics, and discuss any further direction with the team. I am expecting a blanket approval on all of the new code, with particular attention to changes in existing components that I'll point out in a self review. There is also some [ongoing work](https://github.com/smoogipoo/osu/pulls) that may arrive in this branch prior to being merged. --------- Co-authored-by: maarvin <minetoblend@gmail.com> Co-authored-by: Marvin <m.schuerz@hautzy.com> Co-authored-by: Jamie Taylor <me@nekodex.net> Co-authored-by: ArijanJ <arijanj@proton.me> Co-authored-by: Dean Herbert <pe@ppy.sh> Co-authored-by: Tim Oliver <git@tim.dev> Co-authored-by: Joseph Madamba <madamba.joehu@outlook.com> Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com> Co-authored-by: nil <25884226+voidstar0@users.noreply.github.com> Co-authored-by: Ботников Максим <mr.botnikoff@ya.ru> Co-authored-by: Denis Titovets <den232titovets@yandex.ru> Co-authored-by: Michael Middlezong <119022671+mmiddlezong@users.noreply.github.com> Co-authored-by: SupDos <6813986+SupDos@users.noreply.github.com> Co-authored-by: failaip12 <86018517+failaip12@users.noreply.github.com>
238 lines
9.6 KiB
C#
238 lines
9.6 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Framework.Graphics.Primitives;
|
|
using osu.Framework.Graphics.Rendering;
|
|
using osu.Framework.Graphics.Rendering.Vertices;
|
|
using osu.Framework.Graphics.Shaders;
|
|
using osu.Framework.Utils;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Graphics;
|
|
using osu.Game.Graphics.Sprites;
|
|
using osu.Game.Resources.Localisation.Web;
|
|
using osuTK;
|
|
|
|
namespace osu.Game.Screens.Select
|
|
{
|
|
public partial class BeatmapMetadataWedge
|
|
{
|
|
public partial class FailRetryDisplay : CompositeDrawable
|
|
{
|
|
private readonly GraphDrawable retriesGraph;
|
|
private readonly GraphDrawable failsGraph;
|
|
|
|
public APIFailTimes Data
|
|
{
|
|
set
|
|
{
|
|
int[] retries = value.Retries ?? Array.Empty<int>();
|
|
int[] fails = value.Fails ?? Array.Empty<int>();
|
|
int[] total = retries.Zip(fails, (r, f) => r + f).ToArray();
|
|
|
|
int maximum = total.DefaultIfEmpty(0).Max();
|
|
|
|
retriesGraph.Data = total.Select(r => maximum == 0 ? 0 : (float)r / maximum).ToArray();
|
|
failsGraph.Data = fails.Select(r => maximum == 0 ? 0 : (float)r / maximum).ToArray();
|
|
}
|
|
}
|
|
|
|
public FailRetryDisplay()
|
|
{
|
|
RelativeSizeAxes = Axes.X;
|
|
AutoSizeAxes = Axes.Y;
|
|
|
|
InternalChild = new FillFlowContainer
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
AutoSizeAxes = Axes.Y,
|
|
Direction = FillDirection.Vertical,
|
|
Spacing = new Vector2(0f, 4f),
|
|
Children = new Drawable[]
|
|
{
|
|
new OsuSpriteText
|
|
{
|
|
Text = BeatmapsetsStrings.ShowInfoPointsOfFailure,
|
|
Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold),
|
|
Margin = new MarginPadding { Bottom = 4f },
|
|
},
|
|
new Container
|
|
{
|
|
RelativeSizeAxes = Axes.X,
|
|
Height = 65f,
|
|
Children = new[]
|
|
{
|
|
retriesGraph = new GraphDrawable { RelativeSizeAxes = Axes.Both, Y = -1f },
|
|
failsGraph = new GraphDrawable { RelativeSizeAxes = Axes.Both },
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load(OsuColour colours)
|
|
{
|
|
retriesGraph.Colour = colours.Orange1;
|
|
failsGraph.Colour = colours.DarkOrange2;
|
|
}
|
|
|
|
private partial class GraphDrawable : Drawable
|
|
{
|
|
private readonly float[] displayedData = new float[100];
|
|
|
|
private float[] data = new float[100];
|
|
|
|
public float[] Data
|
|
{
|
|
get => data;
|
|
set
|
|
{
|
|
data = value;
|
|
Invalidate(Invalidation.DrawNode);
|
|
}
|
|
}
|
|
|
|
private IShader shader = null!;
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load(ShaderManager shaders)
|
|
{
|
|
shader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "FastCircle");
|
|
}
|
|
|
|
protected override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
bool changed = false;
|
|
|
|
for (int i = 0; i < displayedData.Length; i++)
|
|
{
|
|
float before = displayedData[i];
|
|
float value = data.ElementAtOrDefault(i);
|
|
displayedData[i] = (float)Interpolation.DampContinuously(displayedData[i], value, 40, Time.Elapsed);
|
|
changed |= displayedData[i] != before;
|
|
}
|
|
|
|
if (changed)
|
|
Invalidate(Invalidation.DrawNode);
|
|
}
|
|
|
|
protected override DrawNode CreateDrawNode() => new GraphDrawNode(this);
|
|
|
|
// todo: consider integrating this with BarGraph
|
|
// this is different from BarGraph since this displays each bar with corner radii applied.
|
|
private class GraphDrawNode : DrawNode
|
|
{
|
|
private readonly GraphDrawable source;
|
|
|
|
private Vector2 drawSize;
|
|
private float[] displayedData = null!;
|
|
private IShader shader = null!;
|
|
private IVertexBatch<TexturedVertex2D>? quadBatch;
|
|
|
|
public GraphDrawNode(GraphDrawable source)
|
|
: base(source)
|
|
{
|
|
this.source = source;
|
|
}
|
|
|
|
public override void ApplyState()
|
|
{
|
|
base.ApplyState();
|
|
|
|
drawSize = source.DrawSize;
|
|
displayedData = source.displayedData;
|
|
shader = source.shader;
|
|
}
|
|
|
|
protected override void Draw(IRenderer renderer)
|
|
{
|
|
base.Draw(renderer);
|
|
|
|
const float spacing_constant = 1.5f;
|
|
|
|
float position = 0;
|
|
float barWidth = drawSize.X / displayedData.Length / spacing_constant;
|
|
|
|
float totalSpacing = drawSize.X - barWidth * displayedData.Length;
|
|
float spacing = totalSpacing / (displayedData.Length - 1);
|
|
|
|
quadBatch ??= renderer.CreateQuadBatch<TexturedVertex2D>(displayedData.Length * 4, 1);
|
|
shader.Bind();
|
|
|
|
for (int i = 0; i < displayedData.Length; i++)
|
|
{
|
|
float barHeight = MathF.Max(drawSize.Y * displayedData[i], barWidth);
|
|
|
|
drawBar(renderer, position, barWidth, barHeight);
|
|
|
|
position += barWidth + spacing;
|
|
}
|
|
|
|
shader.Unbind();
|
|
}
|
|
|
|
private void drawBar(IRenderer renderer, float position, float width, float height)
|
|
{
|
|
// Since bars have corner radius, to avoid masking usage and draw all bars in a single draw call
|
|
// we are using FastCircle implementation.
|
|
// Not using FastCircle directly to minimize drawable count.
|
|
|
|
RectangleF drawRectangle = new RectangleF(new Vector2(position, drawSize.Y - height), new Vector2(width, height));
|
|
Vector4 textureRectangle = new Vector4(0, 0, drawRectangle.Width, drawRectangle.Height);
|
|
Quad screenSpaceDrawQuad = Quad.FromRectangle(drawRectangle) * DrawInfo.Matrix;
|
|
|
|
var blend = new Vector2(Math.Min(drawRectangle.Width, drawRectangle.Height) / Math.Min(screenSpaceDrawQuad.Width, screenSpaceDrawQuad.Height));
|
|
|
|
quadBatch?.AddAction(new TexturedVertex2D(renderer)
|
|
{
|
|
Position = screenSpaceDrawQuad.BottomLeft,
|
|
TexturePosition = new Vector2(0, drawRectangle.Height),
|
|
TextureRect = textureRectangle,
|
|
BlendRange = blend,
|
|
Colour = DrawColourInfo.Colour.BottomLeft.SRGB,
|
|
});
|
|
quadBatch?.AddAction(new TexturedVertex2D(renderer)
|
|
{
|
|
Position = screenSpaceDrawQuad.BottomRight,
|
|
TexturePosition = new Vector2(drawRectangle.Width, drawRectangle.Height),
|
|
TextureRect = textureRectangle,
|
|
BlendRange = blend,
|
|
Colour = DrawColourInfo.Colour.BottomRight.SRGB,
|
|
});
|
|
quadBatch?.AddAction(new TexturedVertex2D(renderer)
|
|
{
|
|
Position = screenSpaceDrawQuad.TopRight,
|
|
TexturePosition = new Vector2(drawRectangle.Width, 0),
|
|
TextureRect = textureRectangle,
|
|
BlendRange = blend,
|
|
Colour = DrawColourInfo.Colour.TopRight.SRGB,
|
|
});
|
|
quadBatch?.AddAction(new TexturedVertex2D(renderer)
|
|
{
|
|
Position = screenSpaceDrawQuad.TopLeft,
|
|
TexturePosition = Vector2.Zero,
|
|
TextureRect = textureRectangle,
|
|
BlendRange = blend,
|
|
Colour = DrawColourInfo.Colour.TopLeft.SRGB,
|
|
});
|
|
}
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
{
|
|
base.Dispose(isDisposing);
|
|
|
|
quadBatch?.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|