mirror of
https://github.com/ppy/osu.git
synced 2026-05-14 07:24:04 +08:00
Compare commits
1463 Commits
+7
-5
@@ -1,9 +1,11 @@
|
||||
osu!lazer is currently in early stages of development and is not yet ready for end users. Please avoid creating issues or bugs if you do not personally intend to fix them. Some acceptable topics include:
|
||||
osu!lazer is currently still under heavy development!
|
||||
|
||||
Please ensure that you are making an issue for one of the following:
|
||||
|
||||
- A bug with currently implemented features (not features that don't exist)
|
||||
- A feature you are considering adding, so we can collaborate on feedback and design.
|
||||
- Discussions about technical design decisions
|
||||
- Bugs that you have found and are personally willing and able to fix
|
||||
- TODO lists of smaller tasks around larger features
|
||||
|
||||
Basically, issues are not a place for you to get help. They are a place for developers to collaborate on the game.
|
||||
|
||||
If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide.
|
||||
|
||||
Screenshots and log files are highly welcomed.
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK;
|
||||
using Symcol.Core.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.GameObjects
|
||||
{
|
||||
public class SymcolHitbox : SymcolContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// whether we want to do hit detection
|
||||
/// </summary>
|
||||
public int Team { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// whether we want to do hit detection
|
||||
/// </summary>
|
||||
public bool HitDetection { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// the shape of this object (used for hit detection)
|
||||
/// </summary>
|
||||
public Shape Shape { get; }
|
||||
|
||||
public SymcolHitbox(Vector2 size, Shape shape = Shape.Circle)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Shape = shape;
|
||||
Size = size;
|
||||
|
||||
if (Shape == Shape.Circle)
|
||||
Child = new SymcolContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CornerRadius = Width / 2
|
||||
};
|
||||
else if (Shape == Shape.Rectangle)
|
||||
Child = new SymcolContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
}
|
||||
|
||||
public bool HitDetect(SymcolHitbox hitbox1, SymcolHitbox hitbox2)
|
||||
{
|
||||
if (hitbox1.HitDetection && hitbox2.HitDetection && hitbox1.Team != hitbox2.Team)
|
||||
{
|
||||
if (hitbox1.Shape == Shape.Circle && hitbox2.Shape == Shape.Circle)
|
||||
{
|
||||
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
else if (hitbox1.Shape == Shape.Circle && hitbox2.Shape == Shape.Rectangle || hitbox1.Shape == Shape.Rectangle && hitbox2.Shape == Shape.Circle)
|
||||
{
|
||||
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
else if (hitbox1.Shape == Shape.Rectangle && hitbox2.Shape == Shape.Rectangle)
|
||||
{
|
||||
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
else if (hitbox1.Shape == Shape.Complex || hitbox2.Shape == Shape.Complex)
|
||||
foreach (SymcolContainer child1 in hitbox1.Children)
|
||||
foreach (SymcolContainer child2 in hitbox2.Children)
|
||||
if (child1.ScreenSpaceDrawQuad.AABB.IntersectsWith(child2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Shape
|
||||
{
|
||||
Circle,
|
||||
Rectangle,
|
||||
Complex
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Will support base eden game functions (if we come up with any)
|
||||
/// </summary>
|
||||
public class SymcolClickableContainer : ClickableContainer
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Will support base eden game functions (if we come up with any)
|
||||
/// </summary>
|
||||
public class SymcolContainer : Container
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
public class SymcolDialContainer : CircularContainer
|
||||
{
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
private Vector2 mousePosition;
|
||||
|
||||
private float lastAngle;
|
||||
private float currentRotation;
|
||||
public float RotationAbsolute;
|
||||
|
||||
private int completeTick;
|
||||
|
||||
private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360));
|
||||
|
||||
private bool rotationTransferred;
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
|
||||
|
||||
|
||||
if (!rotationTransferred)
|
||||
{
|
||||
currentRotation = Rotation * 2;
|
||||
rotationTransferred = true;
|
||||
}
|
||||
|
||||
if (thisAngle - lastAngle > 180)
|
||||
lastAngle += 360;
|
||||
else if (lastAngle - thisAngle > 180)
|
||||
lastAngle -= 360;
|
||||
|
||||
currentRotation += thisAngle - lastAngle;
|
||||
RotationAbsolute += Math.Abs(thisAngle - lastAngle);
|
||||
|
||||
lastAngle = thisAngle;
|
||||
|
||||
foreach(Drawable drawable in Children)
|
||||
drawable.RotateTo(currentRotation / 2, 200, Easing.OutExpo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
public class SymcolDragContainer : SymcolContainer
|
||||
{
|
||||
protected override bool OnDragStart(InputState state) => true;
|
||||
|
||||
public bool AllowLeftClickDrag { get; set; } = true;
|
||||
|
||||
private bool drag;
|
||||
|
||||
private Vector2 startPosition;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
startPosition = Position;
|
||||
|
||||
if (args.Button == MouseButton.Left && AllowLeftClickDrag || args.Button == MouseButton.Right)
|
||||
drag = true;
|
||||
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
if (drag)
|
||||
Position = startPosition + state.Mouse.Position - state.Mouse.PositionMouseDown.GetValueOrDefault();
|
||||
|
||||
return base.OnDrag(state);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
if (args.Button == MouseButton.Left && AllowLeftClickDrag || args.Button == MouseButton.Right)
|
||||
drag = false;
|
||||
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using Symcol.Core.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.UserInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// just a Button with a sprite
|
||||
/// </summary>
|
||||
public class SpriteButton : SymcolClickableContainer
|
||||
{
|
||||
private readonly string textureName;
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return spriteText?.Text; }
|
||||
set
|
||||
{
|
||||
if (spriteText != null)
|
||||
spriteText.Text = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Sprite sprite;
|
||||
private readonly SpriteText spriteText;
|
||||
|
||||
public SpriteButton(string textureName)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
this.textureName = textureName;
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
sprite = new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill
|
||||
},
|
||||
spriteText = new SpriteText
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
sprite.Texture = textures.Get(textureName);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
if (Enabled.Value)
|
||||
{
|
||||
var flash = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
};
|
||||
|
||||
Add(flash);
|
||||
|
||||
flash.Blending = BlendingMode.Additive;
|
||||
flash.FadeOut(200);
|
||||
flash.Expire();
|
||||
}
|
||||
|
||||
return base.OnClick(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using Symcol.Core.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.UserInterface
|
||||
{
|
||||
public class SymcolWindow : SymcolContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Put all your stuff in this
|
||||
/// </summary>
|
||||
public SymcolContainer WindowContent { get; set; }
|
||||
public SpriteText WindowTitle;
|
||||
|
||||
private readonly SymcolContainer topBar;
|
||||
private readonly SymcolClickableContainer minimize;
|
||||
|
||||
public SymcolWindow(Vector2 size)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
CornerRadius = 6;
|
||||
Masking = true;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
topBar = new SymcolContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 20,
|
||||
Width = size.X,
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
},
|
||||
WindowTitle = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = 18
|
||||
},
|
||||
new SymcolClickableContainer
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 30,
|
||||
Action = Close,
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.Red,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
}
|
||||
},
|
||||
minimize = new SymcolClickableContainer
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 30,
|
||||
Position = new Vector2(-30, 0),
|
||||
Action = Minimize,
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.White,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
WindowContent = new SymcolContainer
|
||||
{
|
||||
Size = size,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
}
|
||||
};
|
||||
|
||||
WindowContent.Position = new Vector2(0, topBar.Height);
|
||||
}
|
||||
|
||||
protected void Close()
|
||||
{
|
||||
this.FadeOut(200);
|
||||
}
|
||||
|
||||
protected void Open()
|
||||
{
|
||||
this.FadeIn(200);
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
if (Alpha > 0)
|
||||
this.FadeOut(200);
|
||||
else
|
||||
this.FadeIn(200);
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(InputState state) => true;
|
||||
|
||||
private bool drag;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
if (args.Button == MouseButton.Left)
|
||||
drag = true;
|
||||
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
if (drag)
|
||||
Position += state.Mouse.Delta;
|
||||
|
||||
return base.OnDrag(state);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
if (args.Button == MouseButton.Left)
|
||||
drag = false;
|
||||
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
public void Maximize()
|
||||
{
|
||||
WindowContent.FadeIn(200);
|
||||
WindowContent.ScaleTo(Vector2.One, 200);
|
||||
minimize.Action = Minimize;
|
||||
}
|
||||
|
||||
public void Minimize()
|
||||
{
|
||||
WindowContent.FadeOut(200);
|
||||
WindowContent.ScaleTo(new Vector2(1, 0), 200);
|
||||
minimize.Action = Maximize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class BasicPacket : Packet
|
||||
{
|
||||
/// <summary>
|
||||
/// Ask host if we can connect
|
||||
/// </summary>
|
||||
public bool Connect;
|
||||
|
||||
/// <summary>
|
||||
/// Tell the host we are breaking up
|
||||
/// </summary>
|
||||
public bool Disconnect;
|
||||
|
||||
/// <summary>
|
||||
/// Testing Connection
|
||||
/// </summary>
|
||||
public bool Test;
|
||||
|
||||
/// <summary>
|
||||
/// Send a force exit to others
|
||||
/// </summary>
|
||||
public bool Abort;
|
||||
|
||||
/// <summary>
|
||||
/// PreLoad the game
|
||||
/// </summary>
|
||||
public bool LoadGame;
|
||||
|
||||
/// <summary>
|
||||
/// Request a list of all players from Host
|
||||
/// </summary>
|
||||
public bool RequestPlayerList;
|
||||
|
||||
/// <summary>
|
||||
/// List of players in this match that we should account for
|
||||
/// </summary>
|
||||
public List<ClientInfo> PlayerList = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Tell Host we are PreLoaded
|
||||
/// </summary>
|
||||
public bool Loaded;
|
||||
|
||||
/// <summary>
|
||||
/// Start the game already!
|
||||
/// </summary>
|
||||
public bool StartGame;
|
||||
|
||||
/// <summary>
|
||||
/// Send to host when game started
|
||||
/// </summary>
|
||||
public bool GameStarted;
|
||||
|
||||
public BasicPacket(ClientInfo clientInfo) : base(clientInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// Just a client signature basically
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ClientInfo
|
||||
{
|
||||
public string IP;
|
||||
|
||||
public int Port;
|
||||
|
||||
public int Ping;
|
||||
|
||||
public int ConncetionTryCount;
|
||||
|
||||
public double LastConnectionTime;
|
||||
|
||||
public double StartedTestConnectionTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
public class NetworkingClient
|
||||
{
|
||||
public UdpClient UdpClient;
|
||||
|
||||
public IPEndPoint EndPoint;
|
||||
|
||||
/// <summary>
|
||||
/// if false we only receive
|
||||
/// </summary>
|
||||
public readonly bool Send;
|
||||
|
||||
public readonly int Port;
|
||||
|
||||
public readonly string IP;
|
||||
|
||||
public NetworkingClient(bool send, string ip, int port = 25570)
|
||||
{
|
||||
Port = port;
|
||||
IP = ip;
|
||||
|
||||
if (send)
|
||||
initializeSend();
|
||||
else
|
||||
initializeReceive();
|
||||
}
|
||||
|
||||
private void initializeSend()
|
||||
{
|
||||
UdpClient = new UdpClient(IP, Port);
|
||||
}
|
||||
|
||||
private void initializeReceive()
|
||||
{
|
||||
UdpClient = new UdpClient(Port);
|
||||
EndPoint = new IPEndPoint(IPAddress.Any, Port);
|
||||
}
|
||||
|
||||
private void sendByte(byte[] data)
|
||||
{
|
||||
UdpClient.Send(data, data.Length);
|
||||
}
|
||||
|
||||
private byte[] receiveByte()
|
||||
{
|
||||
return UdpClient.Receive(ref EndPoint);
|
||||
}
|
||||
|
||||
public static int SENTPACKETCOUNT;
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet somewhere
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendPacket(Packet packet)
|
||||
{
|
||||
SENTPACKETCOUNT++;
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
formatter.Serialize(stream, packet);
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
int i = packet.PacketSize;
|
||||
retry:
|
||||
byte[] data = new byte[i];
|
||||
|
||||
try
|
||||
{
|
||||
stream.Read(data, 0, (int)stream.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
i *= 2;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
sendByte(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive a Packet from somewhere
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Packet ReceivePacket(bool force = false)
|
||||
{
|
||||
if (UdpClient.Available > 0 || force)
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
byte[] data = receiveByte();
|
||||
stream.Write(data, 0, data.Length);
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
Packet packet = (Packet)formatter.Deserialize(stream);
|
||||
packet.ClientInfo.IP = EndPoint.Address.ToString();
|
||||
|
||||
return packet;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (UdpClient != null)
|
||||
UdpClient.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
#define SoloTesting
|
||||
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
//TODO: This NEEDS its own clock to avoid fuckery later on with DoubleTime and HalfTime
|
||||
public class NetworkingClientHandler : Container
|
||||
{
|
||||
//30 Seconds by default
|
||||
protected virtual double TimeOutTime => 30000;
|
||||
|
||||
protected readonly NetworkingClient ReceiveClient;
|
||||
|
||||
protected readonly NetworkingClient SendClient;
|
||||
|
||||
/// <summary>
|
||||
/// Just a client signature basically
|
||||
/// </summary>
|
||||
public ClientInfo ClientInfo;
|
||||
|
||||
/// <summary>
|
||||
/// All Connecting clients
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> ConnectingClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// All Connected clients
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> ConncetedClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Clients waiting in our match
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> InMatchClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Clients loaded and ready to start
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> LoadedClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Clients ingame playing
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> InGameClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets hit when we get a Packet
|
||||
/// </summary>
|
||||
public Action<Packet> OnPacketReceive;
|
||||
|
||||
/// <summary>
|
||||
/// (Peer) Call this when we connect to a Host (Includes list of connected peers + Host)
|
||||
/// </summary>
|
||||
public Action<List<ClientInfo>> OnConnectedToHost;
|
||||
|
||||
/// <summary>
|
||||
/// (Host) Whenever a new client Connects
|
||||
/// </summary>
|
||||
public Action<ClientInfo> OnClientConnect;
|
||||
|
||||
/// <summary>
|
||||
/// (Host) Whenever a new client Disconnects
|
||||
/// </summary>
|
||||
public Action<ClientInfo> OnClientDisconnect;
|
||||
|
||||
/// <summary>
|
||||
/// (Host/Peer) When a new Client joins the game
|
||||
/// </summary>
|
||||
public Action<ClientInfo> OnClientJoin;
|
||||
|
||||
/// <summary>
|
||||
/// Receive a full player list
|
||||
/// </summary>
|
||||
public Action<List<ClientInfo>> OnReceivePlayerList;
|
||||
|
||||
/// <summary>
|
||||
/// if we are connected and in a match
|
||||
/// </summary>
|
||||
public bool InMatch;
|
||||
|
||||
/// <summary>
|
||||
/// Are we in a game
|
||||
/// </summary>
|
||||
public bool InGame;
|
||||
|
||||
/// <summary>
|
||||
/// Are we loaded and ready to start?
|
||||
/// </summary>
|
||||
public bool Loaded;
|
||||
|
||||
/// <summary>
|
||||
/// Called to leave an in-progress game
|
||||
/// </summary>
|
||||
public Action OnAbort;
|
||||
|
||||
/// <summary>
|
||||
/// Called to load the game (Includes Host)
|
||||
/// </summary>
|
||||
public Action<List<ClientInfo>> OnLoadGame;
|
||||
|
||||
/// <summary>
|
||||
/// Called to start the game once loaded
|
||||
/// </summary>
|
||||
public Action StartGame;
|
||||
|
||||
public readonly ClientType ClientType;
|
||||
|
||||
public NetworkingClientHandler(ClientType type, string ip, int port = 25570, string thisLocalIp = "0.0.0.0")
|
||||
{
|
||||
AlwaysPresent = true;
|
||||
|
||||
ClientType = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ClientType.Host:
|
||||
ReceiveClient = new NetworkingClient(false, ip, port);
|
||||
break;
|
||||
case ClientType.Peer:
|
||||
ReceiveClient = new NetworkingClient(false, thisLocalIp, port);
|
||||
SendClient = new NetworkingClient(true, ip, port);
|
||||
break;
|
||||
case ClientType.Server:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Logger.Log("Created a RulesetNetworkingClientHandler", LoggingTarget.Network, LogLevel.Verbose);
|
||||
|
||||
if (ClientInfo == null)
|
||||
ClientInfo = new ClientInfo
|
||||
{
|
||||
Port = port
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (ClientType == ClientType.Peer)
|
||||
ConnectToHost();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
PacketRestart:
|
||||
Packet p = null;
|
||||
|
||||
if (ReceiveClient.UdpClient.Available > 0)
|
||||
p = ReceiveClient.ReceivePacket();
|
||||
|
||||
if (p is BasicPacket packet)
|
||||
{
|
||||
//Hosts
|
||||
if (SendClient == null)
|
||||
{
|
||||
if (packet.Disconnect)
|
||||
{
|
||||
OnClientDisconnect?.Invoke(packet.ClientInfo);
|
||||
foreach (ClientInfo client in ConnectingClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
ConnectingClients.Remove(client);
|
||||
Logger.Log("A Connecting Client has Disconnected", LoggingTarget.Network, LogLevel.Verbose);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in ConncetedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
ConncetedClients.Remove(client);
|
||||
Logger.Log("A Client has Disconnected", LoggingTarget.Network, LogLevel.Verbose);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in InMatchClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
InMatchClients.Remove(client);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in LoadedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
LoadedClients.Remove(client);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in InGameClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
InGameClients.Remove(client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.Connect)
|
||||
{
|
||||
packet.ClientInfo.StartedTestConnectionTime = Time.Current;
|
||||
ConnectingClients.Add(packet.ClientInfo);
|
||||
|
||||
NetworkingClient client = new NetworkingClient(true, packet.ClientInfo.IP, packet.ClientInfo.Port);
|
||||
|
||||
List<ClientInfo> playerList = new List<ClientInfo>
|
||||
{
|
||||
ClientInfo
|
||||
};
|
||||
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
playerList.Add(clientInfo);
|
||||
|
||||
client.SendPacket(new BasicPacket(ClientInfo)
|
||||
{
|
||||
PlayerList = playerList,
|
||||
Connect = true
|
||||
});
|
||||
|
||||
Logger.Log("A Client is Connecting. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.RequestPlayerList)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, packet.ClientInfo.IP, packet.ClientInfo.Port);
|
||||
|
||||
List<ClientInfo> playerList = new List<ClientInfo>
|
||||
{
|
||||
ClientInfo
|
||||
};
|
||||
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
playerList.Add(clientInfo);
|
||||
|
||||
client.SendPacket(new BasicPacket(ClientInfo)
|
||||
{
|
||||
PlayerList = playerList,
|
||||
RequestPlayerList = true
|
||||
});
|
||||
|
||||
Logger.Log("A Client is Connecting. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.Loaded)
|
||||
foreach (ClientInfo client in InMatchClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
Logger.Log("A Client has Loaded and is ready to start", LoggingTarget.Network, LogLevel.Verbose);
|
||||
InMatchClients.Remove(client);
|
||||
LoadedClients.Add(client);
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet.GameStarted)
|
||||
foreach (ClientInfo client in LoadedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
Logger.Log("A Client has started!", LoggingTarget.Network, LogLevel.Verbose);
|
||||
LoadedClients.Remove(client);
|
||||
InGameClients.Add(client);
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet.Test)
|
||||
{
|
||||
foreach (ClientInfo client in ConnectingClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
client.Ping = (int)Time.Current - (int)client.StartedTestConnectionTime;
|
||||
ConnectingClients.Remove(client);
|
||||
ConncetedClients.Add(client);
|
||||
InMatchClients.Add(client);
|
||||
OnClientJoin?.Invoke(client);
|
||||
client.LastConnectionTime = Time.Current;
|
||||
client.ConncetionTryCount = 0;
|
||||
Logger.Log("Successfully connected to a Client! Ping: " + client.Ping, LoggingTarget.Network, LogLevel.Verbose);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in ConncetedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
client.Ping = (int)Time.Current - (int)client.StartedTestConnectionTime;
|
||||
client.LastConnectionTime = Time.Current;
|
||||
client.ConncetionTryCount = 0;
|
||||
Logger.Log("Successfully maintained connection to a Client! Ping: " + client.Ping, LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !SoloTesting
|
||||
if (InMatchClients.Count == 0 && LoadedClients.Count > 0 && Loaded && !InGame)
|
||||
SendStartGame();
|
||||
#endif
|
||||
|
||||
//Peers
|
||||
else if (SendClient != null)
|
||||
{
|
||||
if (packet.Connect)
|
||||
{
|
||||
if (!InGame && !InMatch)
|
||||
{
|
||||
InMatch = true;
|
||||
OnConnectedToHost?.Invoke(packet.PlayerList);
|
||||
}
|
||||
Logger.Log("Connected to Host!", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.Test)
|
||||
{
|
||||
SendToHost(new BasicPacket(ClientInfo) { Test = true });
|
||||
Logger.Log("Received connection test info from host, returning. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.RequestPlayerList)
|
||||
OnReceivePlayerList?.Invoke(packet.PlayerList);
|
||||
|
||||
if (packet.StartGame)
|
||||
{
|
||||
StartGame?.Invoke();
|
||||
SendToHost(new BasicPacket(ClientInfo) { GameStarted = true });
|
||||
InGame = true;
|
||||
}
|
||||
|
||||
if (packet.Abort)
|
||||
{
|
||||
OnAbort?.Invoke();
|
||||
InGame = false;
|
||||
Loaded = false;
|
||||
}
|
||||
|
||||
if (packet.LoadGame)
|
||||
{
|
||||
Logger.Log("Received instructions to LoadGame for " + packet.PlayerList.Count + " players", LoggingTarget.Network, LogLevel.Verbose);
|
||||
OnLoadGame?.Invoke(packet.PlayerList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SoloTesting
|
||||
if (Loaded && !InGame)
|
||||
SendStartGame();
|
||||
#endif
|
||||
|
||||
if (p != null)
|
||||
OnPacketReceive?.Invoke(p);
|
||||
|
||||
if (ReceiveClient.UdpClient.Available > 0)
|
||||
goto PacketRestart;
|
||||
|
||||
foreach (ClientInfo client in ConnectingClients)
|
||||
{
|
||||
if (client.LastConnectionTime + TimeOutTime / 10 <= Time.Current && client.ConncetionTryCount == 0)
|
||||
{
|
||||
client.StartedTestConnectionTime = Time.Current;
|
||||
TestConnection(client);
|
||||
}
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 6 <= Time.Current && client.ConncetionTryCount == 1)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 3 <= Time.Current && client.ConncetionTryCount == 2)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.StartedTestConnectionTime + TimeOutTime <= Time.Current)
|
||||
{
|
||||
ConnectingClients.Remove(client);
|
||||
Logger.Log("Connection to a connecting client lost! - " + client.IP + ":" + client.Port, LoggingTarget.Network, LogLevel.Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ClientInfo client in ConncetedClients)
|
||||
{
|
||||
if (client.LastConnectionTime + TimeOutTime / 6 <= Time.Current && client.ConncetionTryCount == 0)
|
||||
{
|
||||
client.StartedTestConnectionTime = Time.Current;
|
||||
TestConnection(client);
|
||||
}
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 3 <= Time.Current && client.ConncetionTryCount == 1)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 2 <= Time.Current && client.ConncetionTryCount == 2)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.StartedTestConnectionTime + TimeOutTime <= Time.Current)
|
||||
{
|
||||
ConncetedClients.Remove(client);
|
||||
InGameClients.Remove(client);
|
||||
LoadedClients.Remove(client);
|
||||
InGameClients.Remove(client);
|
||||
Logger.Log("Connection to a connected client lost! - " + client.IP + ":" + client.Port, LoggingTarget.Network, LogLevel.Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Poke!
|
||||
/// </summary>
|
||||
/// <param name="clientInfo"></param>
|
||||
protected void TestConnection(ClientInfo clientInfo)
|
||||
{
|
||||
clientInfo.ConncetionTryCount++;
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(new BasicPacket(ClientInfo) { Test = true });
|
||||
Logger.Log("Testing a client's connection - " + clientInfo.IP + ":" + clientInfo.Port, LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
public void RequestPlayerList()
|
||||
{
|
||||
BasicPacket packet = new BasicPacket(ClientInfo) { RequestPlayerList = true };
|
||||
SendToHost(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell peers to start loading game
|
||||
/// </summary>
|
||||
public virtual void StartLoadingGame()
|
||||
{
|
||||
if (SendClient == null)
|
||||
{
|
||||
BasicPacket packet = new BasicPacket(ClientInfo) { LoadGame = true };
|
||||
|
||||
foreach (ClientInfo client in InMatchClients)
|
||||
packet.PlayerList.Add(client);
|
||||
packet.PlayerList.Add(ClientInfo);
|
||||
|
||||
SendToInMatchClients(packet);
|
||||
|
||||
OnLoadGame?.Invoke(packet.PlayerList);
|
||||
}
|
||||
else
|
||||
Logger.Log("Called StartLoadingGame - We are not the Host!", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this when the game is Loaded and ready to be started
|
||||
/// </summary>
|
||||
public virtual void GameLoaded()
|
||||
{
|
||||
Loaded = true;
|
||||
SendToHost(new BasicPacket(ClientInfo) { Loaded = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the Host
|
||||
/// </summary>
|
||||
public virtual void ConnectToHost()
|
||||
{
|
||||
SendToHost(new BasicPacket(ClientInfo) { Connect = true });
|
||||
Logger.Log("Attempting conection to Host. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell peers to start and starts ours
|
||||
/// </summary>
|
||||
public virtual void SendStartGame()
|
||||
{
|
||||
if (SendClient == null)
|
||||
{
|
||||
SendToLoadedClients(new BasicPacket(ClientInfo) { StartGame = true });
|
||||
InGame = true;
|
||||
Logger.Log("Sending Start Game", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
StartGame?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to the Host
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToHost(Packet packet)
|
||||
{
|
||||
if (SendClient != null)
|
||||
SendClient.SendPacket(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all Connecting clients
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToConnectingClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in ConnectingClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients Connected and waiting
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToConnectedClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients In this Match
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToInMatchClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in InMatchClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients Loaded
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToLoadedClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in LoadedClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients InGame
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToInGameClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in InGameClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to ALL clients we know
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToAllClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
{
|
||||
SendToConnectingClients(packet);
|
||||
SendToConnectedClients(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send to all but the one that sent it
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
/// <param name="playerID"></param>
|
||||
public void ShareWithOtherPeers(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
if (packet.ClientInfo.IP != clientInfo.IP)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AbortGame()
|
||||
{
|
||||
SendToLoadedClients(new BasicPacket(ClientInfo) { Abort = true });
|
||||
SendToInGameClients(new BasicPacket(ClientInfo) { Abort = true });
|
||||
|
||||
restart:
|
||||
foreach (ClientInfo client in LoadedClients)
|
||||
{
|
||||
LoadedClients.Remove(client);
|
||||
InMatchClients.Add(client);
|
||||
goto restart;
|
||||
}
|
||||
foreach (ClientInfo client in InGameClients)
|
||||
{
|
||||
InGameClients.Remove(client);
|
||||
InMatchClients.Add(client);
|
||||
goto restart;
|
||||
}
|
||||
|
||||
InGame = false;
|
||||
Loaded = false;
|
||||
|
||||
OnAbort?.Invoke();
|
||||
}
|
||||
|
||||
public virtual void Disconnect()
|
||||
{
|
||||
Packet packet = new BasicPacket(ClientInfo) { Disconnect = true };
|
||||
|
||||
OnAbort?.Invoke();
|
||||
InMatch = false;
|
||||
InGame = false;
|
||||
Loaded = false;
|
||||
|
||||
if (SendClient == null)
|
||||
{
|
||||
SendToConnectingClients(packet);
|
||||
SendToConnectedClients(packet);
|
||||
}
|
||||
else
|
||||
SendToHost(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Die
|
||||
/// </summary>
|
||||
/// <param name="isDisposing"></param>
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
ReceiveClient?.Clear();
|
||||
|
||||
if (SendClient != null)
|
||||
{
|
||||
SendToHost(new BasicPacket(ClientInfo) { Disconnect = true });
|
||||
SendClient.Clear();
|
||||
}
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ClientType
|
||||
{
|
||||
Host,
|
||||
Peer,
|
||||
Server
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class Packet
|
||||
{
|
||||
/// <summary>
|
||||
/// Just a Signature
|
||||
/// </summary>
|
||||
public readonly ClientInfo ClientInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Specify starting size of packet for efficiency
|
||||
/// </summary>
|
||||
public virtual int PacketSize => 1024;
|
||||
|
||||
public Packet(ClientInfo clientInfo)
|
||||
{
|
||||
ClientInfo = clientInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("symcol.Toys")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("symcol.Toys")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("f34ac16c-e590-4d70-a069-a748326852bf")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F34AC16C-E590-4D70-A069-A748326852BF}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Symcol.Core</RootNamespace>
|
||||
<AssemblyName>Symcol.Core</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Cyotek.Drawing.BitmapFont, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58daa28b0b2de221, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Cyotek.Drawing.BitmapFont.1.3.4-beta1\lib\net46\Cyotek.Drawing.BitmapFont.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ManagedBass, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ManagedBass.2.0.3\lib\net45\ManagedBass.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Generic, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Win32, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLiteNetExtensions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GameObjects\SymcolHitbox.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolClickableContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolDialContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolDragContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolContainer.cs" />
|
||||
<Compile Include="Graphics\UserInterface\SpriteButton.cs" />
|
||||
<Compile Include="Graphics\UserInterface\SymcolWindow.cs" />
|
||||
<Compile Include="Networking\BasicPacket.cs" />
|
||||
<Compile Include="Networking\ClientInfo.cs" />
|
||||
<Compile Include="Networking\NetworkingClient.cs" />
|
||||
<Compile Include="Networking\Packet.cs" />
|
||||
<Compile Include="Networking\NetworkingClientHandler.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-Framework\osu.Framework\osu.Framework.csproj">
|
||||
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
|
||||
<Name>osu.Framework</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,43 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Containers
|
||||
{
|
||||
public class LinkText : OsuSpriteText, IHasTooltip
|
||||
{
|
||||
public string TooltipText => Tooltip;
|
||||
|
||||
public virtual string Tooltip => "";
|
||||
|
||||
private readonly OsuHoverContainer content;
|
||||
|
||||
public override bool HandleKeyboardInput => content.Action != null;
|
||||
public override bool HandleMouseInput => content.Action != null;
|
||||
|
||||
protected override Container<Drawable> Content => content ?? (Container<Drawable>)this;
|
||||
|
||||
public override IEnumerable<Drawable> FlowingChildren => Children;
|
||||
|
||||
public string Url
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
content.Action = () => Process.Start(value);
|
||||
}
|
||||
}
|
||||
|
||||
public LinkText()
|
||||
{
|
||||
AddInternal(content = new OsuHoverContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// TODO: make this more generic
|
||||
/// </summary>
|
||||
public class ProfileLink : LinkText
|
||||
{
|
||||
public override string Tooltip => "View profile in browser";
|
||||
|
||||
public ProfileLink(User user, bool maintainer = false)
|
||||
{
|
||||
if (!maintainer)
|
||||
Text = "Ruleset Creator: " + user.Username;
|
||||
else
|
||||
Text = "Ruleset Maintainer: " + user.Username;
|
||||
|
||||
Url = $@"https://osu.ppy.sh/users/{user.Id}";
|
||||
Font = @"Exo2.0-RegularItalic";
|
||||
TextSize = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.HitObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Mostly stuff copied from Container
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject"></typeparam>
|
||||
public abstract class DrawableSymcolHitObject<TObject> : DrawableHitObject<TObject>
|
||||
where TObject : HitObject
|
||||
{
|
||||
//Future prep?
|
||||
//public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
|
||||
//public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
|
||||
|
||||
protected virtual Container<Drawable> Content => new Container();
|
||||
|
||||
public IReadOnlyList<Drawable> Children
|
||||
{
|
||||
get
|
||||
{
|
||||
return InternalChildren;
|
||||
}
|
||||
set
|
||||
{
|
||||
ChildrenEnumerable = value;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Drawable> ChildrenEnumerable
|
||||
{
|
||||
set
|
||||
{
|
||||
Clear();
|
||||
AddRange(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<Drawable> range)
|
||||
{
|
||||
foreach (Drawable d in range)
|
||||
Add(d);
|
||||
}
|
||||
|
||||
public Drawable Child
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Children.Count != 1)
|
||||
throw new InvalidOperationException($"{nameof(Child)} is only available when there's only 1 in {nameof(Children)}!");
|
||||
|
||||
return Children[0];
|
||||
}
|
||||
set
|
||||
{
|
||||
Clear();
|
||||
Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() => Clear(true);
|
||||
|
||||
public virtual void Clear(bool disposeChildren)
|
||||
{
|
||||
if (Content != null)
|
||||
Content.Clear(disposeChildren);
|
||||
else
|
||||
ClearInternal(disposeChildren);
|
||||
}
|
||||
|
||||
protected DrawableSymcolHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
public void Add(Drawable drawable)
|
||||
{
|
||||
AddInternal(drawable);
|
||||
}
|
||||
|
||||
public void Remove(Drawable drawable)
|
||||
{
|
||||
RemoveInternal(drawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class ChatPacket : Packet
|
||||
{
|
||||
public override int PacketSize => 4096;
|
||||
|
||||
public string Author;
|
||||
|
||||
public string AuthorColor;
|
||||
|
||||
public string Message;
|
||||
|
||||
public ChatPacket(ClientInfo clientInfo) : base(clientInfo)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// Just a client signature basically
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RulesetClientInfo : ClientInfo
|
||||
{
|
||||
public string Username = "";
|
||||
|
||||
public int UserID = -1;
|
||||
|
||||
public string UserPic;
|
||||
|
||||
public string UserBackground;
|
||||
|
||||
public string UserCountry;
|
||||
|
||||
public string CountryFlagName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
//TODO: This NEEDS its own clock to avoid fuckery later on with DoubleTime and HalfTime
|
||||
public class RulesetNetworkingClientHandler : NetworkingClientHandler, IOnlineComponent
|
||||
{
|
||||
public RulesetClientInfo RulesetClientInfo;
|
||||
|
||||
public Action<WorkingBeatmap> OnMapChange;
|
||||
|
||||
private OsuGame osu;
|
||||
|
||||
public RulesetNetworkingClientHandler(ClientType type, string ip, int port = 25570, string thisLocalIp = "0.0.0.0") : base(type, ip, port, thisLocalIp)
|
||||
{
|
||||
if (RulesetClientInfo == null)
|
||||
{
|
||||
RulesetClientInfo = new RulesetClientInfo
|
||||
{
|
||||
Port = port
|
||||
};
|
||||
|
||||
ClientInfo = RulesetClientInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send Map to Peers
|
||||
/// </summary>
|
||||
/// <param name="map"></param>
|
||||
public void SetMap(WorkingBeatmap map)
|
||||
{
|
||||
RulesetPacket packet;
|
||||
try
|
||||
{
|
||||
packet = new RulesetPacket(RulesetClientInfo)
|
||||
{
|
||||
OnlineBeatmapSetID = (int)map.BeatmapSetInfo.OnlineBeatmapSetID,
|
||||
OnlineBeatmapID = (int)map.BeatmapInfo.OnlineBeatmapID
|
||||
};
|
||||
SendToInMatchClients(packet);
|
||||
OnMapChange?.Invoke(osu.Beatmap.Value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
packet = new RulesetPacket(RulesetClientInfo);
|
||||
SendToInMatchClients(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(APIAccess api, OsuGame osu)
|
||||
{
|
||||
api.Register(this);
|
||||
this.osu = osu;
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
RulesetClientInfo.Username = "";
|
||||
RulesetClientInfo.UserID = -1;
|
||||
break;
|
||||
case APIState.Online:
|
||||
RulesetClientInfo.Username = api.LocalUser.Value.Username;
|
||||
RulesetClientInfo.UserID = (int)api.LocalUser.Value.Id;
|
||||
RulesetClientInfo.UserCountry = api.LocalUser.Value.Country.FullName;
|
||||
RulesetClientInfo.CountryFlagName = api.LocalUser.Value.Country.FlagName;
|
||||
RulesetClientInfo.UserPic = api.LocalUser.Value.AvatarUrl;
|
||||
RulesetClientInfo.UserBackground = api.LocalUser.Value.CoverUrl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class RulesetPacket : Packet
|
||||
{
|
||||
public new readonly RulesetClientInfo ClientInfo;
|
||||
|
||||
public override int PacketSize => 4096;
|
||||
|
||||
public int OnlineBeatmapSetID = -1;
|
||||
|
||||
public int OnlineBeatmapID = -1;
|
||||
|
||||
public bool HaveMap;
|
||||
|
||||
public string ChatContent;
|
||||
|
||||
//public string RulesetName = "";
|
||||
|
||||
public RulesetPacket(RulesetClientInfo rulesetClientInfo) : base(rulesetClientInfo)
|
||||
{
|
||||
ClientInfo = rulesetClientInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class ScorePacket : Packet
|
||||
{
|
||||
public override int PacketSize => 2048;
|
||||
|
||||
public int Score;
|
||||
|
||||
public ScorePacket(ClientInfo clientInfo, int score) : base(clientInfo)
|
||||
{
|
||||
Score = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Options
|
||||
{
|
||||
public class MultiplayerDropdownEnumOption<T> : MultiplayerOption
|
||||
where T : struct
|
||||
{
|
||||
public readonly Bindable<T> BindableEnum;
|
||||
|
||||
public MultiplayerDropdownEnumOption(Bindable<T> bindable, string name, int quadrant, bool sync = true) : base(name, quadrant, sync)
|
||||
{
|
||||
BindableEnum = bindable;
|
||||
|
||||
OptionContainer.Child = new BetterSettingsEnumDropdown<T>
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Bindable = bindable,
|
||||
};
|
||||
}
|
||||
|
||||
private class BetterSettingsEnumDropdown<T> : SettingsEnumDropdown<T>
|
||||
{
|
||||
protected override Drawable CreateControl() => new BetterOsuEnumDropdown<T>
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
};
|
||||
|
||||
private class BetterOsuEnumDropdown<T> : OsuEnumDropdown<T>
|
||||
{
|
||||
public BetterOsuEnumDropdown()
|
||||
{
|
||||
Menu.MaxHeight = 160;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Options
|
||||
{
|
||||
public abstract class MultiplayerOption : Container
|
||||
{
|
||||
protected readonly SpriteText Title;
|
||||
|
||||
protected readonly Container OptionContainer;
|
||||
|
||||
public MultiplayerOption(string name, int quadrant, bool sync = true)
|
||||
{
|
||||
if (quadrant == 1 | quadrant == 3 | quadrant == 5 | quadrant == 7)
|
||||
{
|
||||
switch (quadrant)
|
||||
{
|
||||
case 1:
|
||||
quadrant = 0;
|
||||
break;
|
||||
case 3:
|
||||
quadrant = 1;
|
||||
break;
|
||||
case 5:
|
||||
quadrant = 2;
|
||||
break;
|
||||
case 7:
|
||||
quadrant = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
Position = new Vector2(16, 4 + (64 * quadrant));
|
||||
}
|
||||
else if (quadrant == 2 | quadrant == 4 | quadrant == 6 | quadrant == 8)
|
||||
{
|
||||
switch (quadrant)
|
||||
{
|
||||
case 2:
|
||||
quadrant = 0;
|
||||
break;
|
||||
case 4:
|
||||
quadrant = 1;
|
||||
break;
|
||||
case 6:
|
||||
quadrant = 2;
|
||||
break;
|
||||
case 8:
|
||||
quadrant = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopLeft;
|
||||
Position = new Vector2(22, 4 + (64 * quadrant));
|
||||
}
|
||||
else
|
||||
throw new Exception("Globglogabgalab");
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Width = 0.49f;
|
||||
Height = 80;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Title = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
TextSize = 20,
|
||||
Text = name
|
||||
},
|
||||
OptionContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(-16, 18),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Options
|
||||
{
|
||||
public class MultiplayerToggleOption : MultiplayerOption
|
||||
{
|
||||
public readonly Bindable<bool> BindableBool;
|
||||
|
||||
public MultiplayerToggleOption(Bindable<bool> bindable, string name, int quadrant, bool sync = true) : base(name, quadrant, sync)
|
||||
{
|
||||
BindableBool = bindable;
|
||||
|
||||
Child = new SettingsCheckbox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Bindable = bindable,
|
||||
LabelText = " " + name,
|
||||
Position = new Vector2(-16, 18),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Users;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class Chat : Container, IOnlineComponent
|
||||
{
|
||||
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
|
||||
|
||||
private string playerColorHex = SymcolSettingsSubsection.SymcolConfigManager.GetBindable<string>(SymcolSetting.PlayerColor);
|
||||
|
||||
private User user;
|
||||
|
||||
private readonly FillFlowContainer<ChatMessage> messageContainer;
|
||||
private readonly OsuTextBox textBox;
|
||||
|
||||
public Chat(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
|
||||
rulesetNetworkingClientHandler.OnPacketReceive += (packet) =>
|
||||
{
|
||||
if (packet is ChatPacket chatPacket)
|
||||
Add(chatPacket);
|
||||
if (rulesetNetworkingClientHandler.ClientType == ClientType.Host)
|
||||
rulesetNetworkingClientHandler.ShareWithOtherPeers(packet);
|
||||
};
|
||||
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Height = 0.46f;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.8f
|
||||
},
|
||||
new OsuScrollContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.9f,
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
messageContainer = new FillFlowContainer<ChatMessage>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
},
|
||||
textBox = new OsuTextBox
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.98f,
|
||||
Height = 36,
|
||||
Position = new Vector2(0, -12),
|
||||
Colour = Color4.White,
|
||||
Text = "Type here!"
|
||||
}
|
||||
};
|
||||
|
||||
textBox.OnCommit += (s, r) =>
|
||||
{
|
||||
AddMessage(textBox.Text);
|
||||
textBox.Text = "";
|
||||
};
|
||||
}
|
||||
|
||||
public void Add(ChatPacket packet)
|
||||
{
|
||||
ChatMessage message = new ChatMessage(packet);
|
||||
messageContainer.Add(message);
|
||||
}
|
||||
|
||||
public void AddMessage(string message)
|
||||
{
|
||||
if (message == "" | message == " ")
|
||||
return;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
OsuColour.FromHex(playerColorHex);
|
||||
}
|
||||
catch
|
||||
{
|
||||
playerColorHex = "#ffffff";
|
||||
}
|
||||
|
||||
ChatPacket packet = new ChatPacket(rulesetNetworkingClientHandler.ClientInfo)
|
||||
{
|
||||
Author = user.Username,
|
||||
AuthorColor = playerColorHex,
|
||||
Message = message,
|
||||
};
|
||||
|
||||
rulesetNetworkingClientHandler.SendToHost(packet);
|
||||
rulesetNetworkingClientHandler.SendToInMatchClients(packet);
|
||||
Add(packet);
|
||||
}
|
||||
else
|
||||
Logger.Log("You must be logged in to message!", LoggingTarget.Network, LogLevel.Error);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(APIAccess api)
|
||||
{
|
||||
api.Register(this);
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
user = null;
|
||||
break;
|
||||
case APIState.Online:
|
||||
user = api.LocalUser.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class ChatMessage : Container
|
||||
{
|
||||
public ChatMessage(ChatPacket packet)
|
||||
{
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = OsuColour.FromHex(packet.AuthorColor),
|
||||
TextSize = 24,
|
||||
Text = packet.Author + ":"
|
||||
},
|
||||
new OsuTextFlowContainer(t => { t.TextSize = 24; })
|
||||
{
|
||||
Position = new OpenTK.Vector2(140, 0),
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = packet.Message
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Users;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MatchPlayer : ClickableContainer, IHasContextMenu
|
||||
{
|
||||
public readonly RulesetClientInfo ClientInfo;
|
||||
|
||||
private readonly Box dim;
|
||||
|
||||
private readonly DrawableFlag countryFlag;
|
||||
private readonly UserCoverBackground profileBackground;
|
||||
private readonly UpdateableAvatar profilePicture;
|
||||
|
||||
public MatchPlayer(RulesetClientInfo clientInfo)
|
||||
{
|
||||
ClientInfo = clientInfo;
|
||||
|
||||
Alpha = 0;
|
||||
Masking = true;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 40f;
|
||||
CornerRadius = 10;
|
||||
|
||||
Country country = new Country
|
||||
{
|
||||
FullName = ClientInfo.UserCountry,
|
||||
FlagName = ClientInfo.CountryFlagName,
|
||||
};
|
||||
|
||||
User user = new User
|
||||
{
|
||||
Username = ClientInfo.Username,
|
||||
Id = ClientInfo.UserID,
|
||||
Country = country,
|
||||
AvatarUrl = ClientInfo.UserPic,
|
||||
CoverUrl = ClientInfo.UserBackground,
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
profileBackground = new UserCoverBackground(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill,
|
||||
OnLoadComplete = d => d.FadeInFromZero(200),
|
||||
},
|
||||
dim = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.8f
|
||||
},
|
||||
profilePicture = new UpdateableAvatar
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = new Vector2(Height * 0.8f),
|
||||
Position = new Vector2(6, 0),
|
||||
User = user,
|
||||
Masking = true,
|
||||
CornerRadius = 6,
|
||||
},
|
||||
countryFlag = new DrawableFlag(country)
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = new Vector2(Height * 0.9f, (Height * 0.9f) * 0.66f),
|
||||
Position = new Vector2(-10, 0)
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Position = new Vector2(Height * 1.1f, 0),
|
||||
TextSize = Height * 0.9f,
|
||||
Text = user.Username
|
||||
}
|
||||
};
|
||||
|
||||
Action = () =>
|
||||
{
|
||||
Process.Start("https://osu.ppy.sh/users/" + user.Id);
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
dim.FadeTo(0.6f, 200);
|
||||
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
base.OnHoverLost(state);
|
||||
|
||||
dim.FadeTo(0.8f, 200);
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem("View Profile", MenuItemType.Standard, () => { }),
|
||||
new OsuMenuItem("Promote to Host", MenuItemType.Highlighted, () => { }),
|
||||
new OsuMenuItem("Kick", MenuItemType.Destructive, () => { }),
|
||||
new OsuMenuItem("Ban", MenuItemType.Destructive, () => { }),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MatchPlayerList : Container
|
||||
{
|
||||
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
|
||||
|
||||
public readonly List<MatchPlayer> MatchPlayers = new List<MatchPlayer>();
|
||||
|
||||
public readonly FillFlowContainer MatchPlayersContianer;
|
||||
|
||||
public MatchPlayerList(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 16;
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Width = 0.49f;
|
||||
Height = 0.45f;
|
||||
Position = new Vector2(10);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.8f)
|
||||
},
|
||||
MatchPlayersContianer = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.98f,
|
||||
Height = 0.96f
|
||||
}
|
||||
};
|
||||
|
||||
rulesetNetworkingClientHandler.OnReceivePlayerList += (players) =>
|
||||
{
|
||||
restart:
|
||||
foreach (MatchPlayer matchPlayer in MatchPlayers)
|
||||
foreach (ClientInfo clientInfo in players)
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
if (rulesetClientInfo.IP + rulesetClientInfo.Port != matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
|
||||
{
|
||||
Add(rulesetClientInfo);
|
||||
players.Remove(clientInfo);
|
||||
goto restart;
|
||||
}
|
||||
};
|
||||
rulesetNetworkingClientHandler.RequestPlayerList();
|
||||
|
||||
rulesetNetworkingClientHandler.OnClientJoin += (clientInfo) =>
|
||||
{
|
||||
foreach (MatchPlayer matchPlayer in MatchPlayers)
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
if (rulesetClientInfo.IP + rulesetClientInfo.Port != matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
|
||||
{
|
||||
Add(rulesetClientInfo);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
rulesetNetworkingClientHandler.OnClientDisconnect += (clientInfo) =>
|
||||
{
|
||||
foreach (MatchPlayer matchPlayer in MatchPlayers)
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
if (rulesetClientInfo.IP + rulesetClientInfo.Port == matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
|
||||
{
|
||||
Remove(matchPlayer);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Add(RulesetClientInfo clientInfo)
|
||||
{
|
||||
MatchPlayer matchPlayer = new MatchPlayer(clientInfo);
|
||||
|
||||
Add(matchPlayer);
|
||||
}
|
||||
|
||||
public void Add(MatchPlayer matchPlayer)
|
||||
{
|
||||
MatchPlayers.Add(matchPlayer);
|
||||
MatchPlayersContianer.Add(matchPlayer);
|
||||
matchPlayer.FadeInFromZero(200);
|
||||
}
|
||||
|
||||
public void Remove(MatchPlayer matchPlayer)
|
||||
{
|
||||
MatchPlayers.Remove(matchPlayer);
|
||||
matchPlayer.FadeOutFromOne(200)
|
||||
.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Screens.Symcol.Pieces;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Options;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MatchTools : Container
|
||||
{
|
||||
public readonly Bindable<MatchScreenMode> Mode = new Bindable<MatchScreenMode>() { Default = MatchScreenMode.MapDetails };
|
||||
|
||||
public readonly Bindable<MatchGamemode> GameMode = new Bindable<MatchGamemode>() { Default = MatchGamemode.HeadToHead };
|
||||
|
||||
public readonly OsuTabControl<MatchScreenMode> TabControl;
|
||||
|
||||
public readonly Container SelectedContent;
|
||||
|
||||
public readonly Container MapDetails;
|
||||
|
||||
public Container RulesetSettings;
|
||||
|
||||
public readonly Container SoundBoard;
|
||||
|
||||
private WorkingBeatmap selectedBeatmap;
|
||||
|
||||
private int selectedBeatmapSetID;
|
||||
|
||||
public MatchTools()
|
||||
{
|
||||
Masking = true;
|
||||
CornerRadius = 16;
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Width = 0.49f;
|
||||
Height = 0.45f;
|
||||
Position = new Vector2(-10, 10);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.8f)
|
||||
},
|
||||
TabControl = new OsuTabControl<MatchScreenMode>
|
||||
{
|
||||
Position = new Vector2(72, 0),
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.08f,
|
||||
Width = 0.8f
|
||||
},
|
||||
SelectedContent = new Container
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.92f
|
||||
}
|
||||
};
|
||||
TabControl.Current.Value = MatchScreenMode.MapDetails;
|
||||
|
||||
Mode.ValueChanged += (value) =>
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case MatchScreenMode.MapDetails:
|
||||
if (selectedBeatmap != null)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmap);
|
||||
else if (selectedBeatmapSetID != 0)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmapSetID);
|
||||
else
|
||||
SelectedContent.Child = new MapDetailsSection(true);
|
||||
break;
|
||||
case MatchScreenMode.MatchSettings:
|
||||
SelectedContent.Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MultiplayerDropdownEnumOption<MatchGamemode>(GameMode, "Match Gamemode", 1)
|
||||
}
|
||||
};
|
||||
break;
|
||||
case MatchScreenMode.SoundBoard:
|
||||
SelectedContent.Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new HitSoundBoard
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
ButtonSize = 80
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
};
|
||||
Mode.BindTo(TabControl.Current);
|
||||
}
|
||||
|
||||
public void MapChange(WorkingBeatmap workingBeatmap)
|
||||
{
|
||||
if (workingBeatmap == null)
|
||||
{
|
||||
MapChange(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
selectedBeatmap = workingBeatmap;
|
||||
selectedBeatmapSetID = (int)workingBeatmap.BeatmapSetInfo.OnlineBeatmapSetID;
|
||||
|
||||
if (Mode.Value == MatchScreenMode.MapDetails)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmap);
|
||||
}
|
||||
|
||||
public void MapChange(int onlineBeatmapSetID)
|
||||
{
|
||||
selectedBeatmap = null;
|
||||
selectedBeatmapSetID = onlineBeatmapSetID;
|
||||
|
||||
if (Mode.Value == MatchScreenMode.MapDetails)
|
||||
{
|
||||
if (selectedBeatmapSetID != 0 && selectedBeatmapSetID != -1)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmapSetID);
|
||||
else
|
||||
SelectedContent.Child = new MapDetailsSection(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MapDetailsSection : ClickableContainer
|
||||
{
|
||||
private Sprite beatmapBG;
|
||||
private SpriteText name;
|
||||
private SpriteText artist;
|
||||
private SpriteText difficulty;
|
||||
private SpriteText time;
|
||||
|
||||
private Box dim;
|
||||
|
||||
public MapDetailsSection(WorkingBeatmap workingBeatmap)
|
||||
{
|
||||
draw();
|
||||
|
||||
HitObject lastObject = workingBeatmap.Beatmap.HitObjects.LastOrDefault();
|
||||
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
|
||||
|
||||
beatmapBG.Texture = workingBeatmap.Background;
|
||||
name.Text = workingBeatmap.BeatmapSetInfo.Metadata.Title;
|
||||
artist.Text = "By: " + workingBeatmap.BeatmapSetInfo.Metadata.Artist;
|
||||
difficulty.Text = workingBeatmap.BeatmapInfo.Version + " (" + Math.Round(workingBeatmap.BeatmapInfo.StarDifficulty, 2) + " stars) mapped by " + workingBeatmap.BeatmapInfo.Metadata.AuthorString;
|
||||
time.Text = getBPMRange(workingBeatmap.Beatmap) + " bpm for " + TimeSpan.FromMilliseconds(endTime - workingBeatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss");
|
||||
|
||||
BorderColour = getColour(workingBeatmap.BeatmapInfo);
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Radius = 16,
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = getColour(workingBeatmap.BeatmapInfo).Opacity(0.2f)
|
||||
};
|
||||
Action = () => Process.Start("https://osu.ppy.sh/beatmapsets/" + workingBeatmap.BeatmapSetInfo.OnlineBeatmapSetID);
|
||||
}
|
||||
|
||||
public MapDetailsSection(int onlineBeatmapSetID)
|
||||
{
|
||||
draw();
|
||||
name.Text = "Missing Map!";
|
||||
artist.Text = "Click to open in Browser";
|
||||
Action = () => Process.Start("https://osu.ppy.sh/beatmapsets/" + onlineBeatmapSetID);
|
||||
}
|
||||
|
||||
public MapDetailsSection(bool invalid)
|
||||
{
|
||||
draw();
|
||||
name.Text = "Invalid / No Map Selected!";
|
||||
artist.Text = "Don't hit start, weird things might happen";
|
||||
Action = () => Process.Start("https://osu.ppy.sh/home");
|
||||
}
|
||||
|
||||
private void draw()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Width = 0.95f;
|
||||
Height = 0.9f;
|
||||
|
||||
Masking = true;
|
||||
BorderColour = Color4.LightBlue;
|
||||
BorderThickness = 4;
|
||||
CornerRadius = 10;
|
||||
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Radius = 16,
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.LightBlue.Opacity(0.2f)
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
beatmapBG = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
},
|
||||
dim = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f
|
||||
},
|
||||
name = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 0),
|
||||
Font = @"Exo2.0-SemiBoldItalic",
|
||||
TextSize = 40
|
||||
},
|
||||
artist = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 38),
|
||||
Font = @"Exo2.0-MediumItalic",
|
||||
TextSize = 24
|
||||
},
|
||||
difficulty = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 64),
|
||||
Font = "Exo2.0-Bold",
|
||||
TextSize = 16
|
||||
},
|
||||
time = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 84),
|
||||
TextSize = 16
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
dim.FadeTo(0.4f, 200);
|
||||
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
base.OnHoverLost(state);
|
||||
|
||||
dim.FadeTo(0.6f, 200);
|
||||
}
|
||||
|
||||
//"Borrowed" stuff
|
||||
private string getBPMRange(Beatmap beatmap)
|
||||
{
|
||||
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
|
||||
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
|
||||
|
||||
if (Precision.AlmostEquals(bpmMin, bpmMax))
|
||||
return $"{bpmMin:0}";
|
||||
|
||||
return $"{bpmMin:0}-{bpmMax:0} (mostly {beatmap.ControlPointInfo.BPMMode:0})";
|
||||
}
|
||||
|
||||
private enum DifficultyRating
|
||||
{
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
Insane,
|
||||
Expert,
|
||||
ExpertPlus
|
||||
}
|
||||
|
||||
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
|
||||
{
|
||||
if (beatmap == null)
|
||||
throw new ArgumentNullException(nameof(beatmap));
|
||||
|
||||
var rating = beatmap.StarDifficulty;
|
||||
|
||||
if (rating < 1.5) return DifficultyRating.Easy;
|
||||
if (rating < 2.25) return DifficultyRating.Normal;
|
||||
if (rating < 3.75) return DifficultyRating.Hard;
|
||||
if (rating < 5.25) return DifficultyRating.Insane;
|
||||
if (rating < 6.75) return DifficultyRating.Expert;
|
||||
return DifficultyRating.ExpertPlus;
|
||||
}
|
||||
|
||||
private Color4 getColour(BeatmapInfo beatmap)
|
||||
{
|
||||
OsuColour palette = new OsuColour();
|
||||
switch (getDifficultyRating(beatmap))
|
||||
{
|
||||
case DifficultyRating.Easy:
|
||||
return palette.Green;
|
||||
default:
|
||||
case DifficultyRating.Normal:
|
||||
return palette.Blue;
|
||||
case DifficultyRating.Hard:
|
||||
return palette.Yellow;
|
||||
case DifficultyRating.Insane:
|
||||
return palette.Pink;
|
||||
case DifficultyRating.Expert:
|
||||
return palette.Purple;
|
||||
case DifficultyRating.ExpertPlus:
|
||||
return palette.Gray0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum MatchGamemode
|
||||
{
|
||||
[System.ComponentModel.Description("Head to Head")]
|
||||
HeadToHead,
|
||||
[System.ComponentModel.Description("Head to Head with Live Spectator")]
|
||||
HeadToHeadSpectator,
|
||||
[System.ComponentModel.Description("Team Versus")]
|
||||
TeamVS,
|
||||
[System.ComponentModel.Description("TAG4")]
|
||||
TAG4,
|
||||
[System.ComponentModel.Description("Team TAG4")]
|
||||
TeamTAG4,
|
||||
[System.ComponentModel.Description("Tourny Mode")]
|
||||
Tournement,
|
||||
}
|
||||
|
||||
public enum MatchScreenMode
|
||||
{
|
||||
[System.ComponentModel.Description("Map Details")]
|
||||
MapDetails,
|
||||
[System.ComponentModel.Description("Match Settings")]
|
||||
MatchSettings,
|
||||
[System.ComponentModel.Description("Ruleset Settings")]
|
||||
RulesetSettings,
|
||||
[System.ComponentModel.Description("Sound Board")]
|
||||
SoundBoard
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MultiplayerScoreboard : Container
|
||||
{
|
||||
public readonly Container<MultiplayerScoreboardItem> ScoreboardItems;
|
||||
|
||||
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
|
||||
|
||||
private readonly ScoreProcessor scoreProcessor;
|
||||
|
||||
private double updateScoreTime = 0;
|
||||
|
||||
public MultiplayerScoreboard(RulesetNetworkingClientHandler rulesetNetworkingClientHandler, List<ClientInfo> playerList, ScoreProcessor scoreProcessor)
|
||||
{
|
||||
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
this.scoreProcessor = scoreProcessor;
|
||||
|
||||
AlwaysPresent = true;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Width = 120;
|
||||
|
||||
Position = new Vector2(0, -200);
|
||||
Anchor = Anchor.CentreLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
|
||||
Child = ScoreboardItems = new Container<MultiplayerScoreboardItem>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
};
|
||||
|
||||
int i = 1;
|
||||
foreach (ClientInfo clientInfo in playerList)
|
||||
{
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
{
|
||||
ScoreboardItems.Add(new MultiplayerScoreboardItem(rulesetClientInfo, i) { });
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
rulesetNetworkingClientHandler.OnPacketReceive += (Packet packet) =>
|
||||
{
|
||||
if (packet is ScorePacket scorePacket)
|
||||
{
|
||||
rulesetNetworkingClientHandler.ShareWithOtherPeers(scorePacket);
|
||||
foreach (MultiplayerScoreboardItem item in ScoreboardItems)
|
||||
if (scorePacket.ClientInfo == item.ClientInfo)
|
||||
item.Score = scorePacket.Score;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Time.Current >= updateScoreTime)
|
||||
{
|
||||
updateScoreTime = Time.Current + 500;
|
||||
foreach (MultiplayerScoreboardItem item in ScoreboardItems)
|
||||
if (rulesetNetworkingClientHandler.ClientInfo == item.ClientInfo)
|
||||
item.Score = (int)scoreProcessor.TotalScore.Value;
|
||||
|
||||
rulesetNetworkingClientHandler.SendToHost(new ScorePacket(rulesetNetworkingClientHandler.ClientInfo, (int)scoreProcessor.TotalScore.Value));
|
||||
rulesetNetworkingClientHandler.SendToInGameClients(new ScorePacket(rulesetNetworkingClientHandler.ClientInfo, (int)scoreProcessor.TotalScore.Value));
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (args.Key == Key.Tab)
|
||||
{
|
||||
if (Alpha > 0)
|
||||
this.FadeOut(100);
|
||||
else
|
||||
this.FadeIn(100);
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MultiplayerScoreboardItem : Container
|
||||
{
|
||||
public int Score
|
||||
{
|
||||
get { return score; }
|
||||
set
|
||||
{
|
||||
if (value != score)
|
||||
{
|
||||
score = value;
|
||||
scoreText.Text = value.ToString();
|
||||
|
||||
foreach(MultiplayerScoreboardItem item in itemList)
|
||||
if (value > item.Score && Place > item.Place)
|
||||
{
|
||||
Place = item.Place;
|
||||
foreach (MultiplayerScoreboardItem i in itemList)
|
||||
if (i.Place < Place)
|
||||
i.Place -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Place
|
||||
{
|
||||
get { return place; }
|
||||
set
|
||||
{
|
||||
if (Place != place)
|
||||
{
|
||||
place = value;
|
||||
this.MoveTo(new Vector2(0, (-height - 8) * (value - 1)), 200, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int place = 0;
|
||||
|
||||
private int score = 0;
|
||||
|
||||
private const int height = 60;
|
||||
|
||||
public readonly RulesetClientInfo ClientInfo;
|
||||
|
||||
private readonly SpriteText scoreText;
|
||||
|
||||
private static List<MultiplayerScoreboardItem> itemList = new List<MultiplayerScoreboardItem>();
|
||||
|
||||
public MultiplayerScoreboardItem(RulesetClientInfo clientInfo, int place)
|
||||
{
|
||||
ClientInfo = clientInfo;
|
||||
this.place = place;
|
||||
|
||||
itemList.Add(this);
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 8;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.8f,
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(4),
|
||||
Text = clientInfo.Username
|
||||
},
|
||||
scoreText = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Position = new Vector2(-4),
|
||||
Text = Score.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
this.MoveTo(new Vector2(0, (-height - 8) * (Place - 1)), 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
itemList.Remove(this);
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Select;
|
||||
using System;
|
||||
using osu.Game.Screens;
|
||||
using osu.Framework.Screens;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public class MatchSongSelect : SongSelect
|
||||
{
|
||||
public WorkingBeatmap SelectedMap;
|
||||
|
||||
private bool exiting;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => null;
|
||||
|
||||
public Action Action;
|
||||
|
||||
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
public MatchSongSelect(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
base.OnEntering(last);
|
||||
}
|
||||
|
||||
protected override bool OnSelectionFinalised()
|
||||
{
|
||||
if (!exiting)
|
||||
{
|
||||
RulesetNetworkingClientHandler.OnMapChange?.Invoke(null);
|
||||
SelectedMap = Beatmap.Value;
|
||||
Action();
|
||||
exiting = true;
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
Exit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK.Input;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Pieces;
|
||||
using System.Collections.Generic;
|
||||
using Symcol.Core.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public class MultiPlayer : ScreenWithBeatmapBackground, IProvideCursor
|
||||
{
|
||||
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
|
||||
|
||||
protected override float BackgroundParallaxAmount => 0.1f;
|
||||
|
||||
public override bool ShowOverlaysOnEnter => false;
|
||||
|
||||
public Action RestartRequested;
|
||||
|
||||
public override bool AllowBeatmapRulesetChange => false;
|
||||
|
||||
public bool HasFailed { get; private set; }
|
||||
|
||||
public bool AllowPause { get; set; } = false;
|
||||
public bool AllowLeadIn { get; set; } = true;
|
||||
public bool AllowResults { get; set; } = true;
|
||||
|
||||
public int RestartCount;
|
||||
|
||||
public CursorContainer Cursor => RulesetContainer.Cursor;
|
||||
public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded.Value;
|
||||
|
||||
private IAdjustableClock sourceClock;
|
||||
private DecoupleableInterpolatingFramedClock adjustableClock;
|
||||
|
||||
private RulesetInfo ruleset;
|
||||
|
||||
private APIAccess api;
|
||||
|
||||
private ScoreProcessor scoreProcessor;
|
||||
protected RulesetContainer RulesetContainer;
|
||||
|
||||
#region User Settings
|
||||
|
||||
private Bindable<double> dimLevel;
|
||||
private Bindable<double> blurLevel;
|
||||
private Bindable<bool> showStoryboard;
|
||||
private Bindable<bool> mouseWheelDisabled;
|
||||
private Bindable<double> userAudioOffset;
|
||||
|
||||
private SampleChannel sampleRestart;
|
||||
|
||||
#endregion
|
||||
|
||||
private Container storyboardContainer;
|
||||
private DrawableStoryboard storyboard;
|
||||
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
private MultiplayerScoreboard scoreboard;
|
||||
|
||||
private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true;
|
||||
|
||||
private readonly List<ClientInfo> playerList;
|
||||
|
||||
public MultiPlayer(RulesetNetworkingClientHandler rulesetNetworkingClientHandler, List<ClientInfo> playerList)//, WorkingBeatmap beatmap = null)
|
||||
{
|
||||
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
RulesetNetworkingClientHandler.OnAbort = () => Exit();
|
||||
RulesetNetworkingClientHandler.StartGame = () => start();
|
||||
this.playerList = playerList;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, APIAccess api, OsuConfigManager config)
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
dimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
|
||||
blurLevel = config.GetBindable<double>(OsuSetting.BlurLevel);
|
||||
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
|
||||
sampleRestart = audio.Sample.Get(@"Gameplay/restart");
|
||||
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
|
||||
|
||||
WorkingBeatmap working = Beatmap.Value;
|
||||
Beatmap beatmap;
|
||||
|
||||
try
|
||||
{
|
||||
beatmap = working.Beatmap;
|
||||
|
||||
if (beatmap == null)
|
||||
throw new InvalidOperationException("Beatmap was not loaded");
|
||||
|
||||
ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset;
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
try
|
||||
{
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
// we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset
|
||||
// let's try again forcing the beatmap's ruleset.
|
||||
ruleset = beatmap.BeatmapInfo.Ruleset;
|
||||
rulesetInstance = ruleset.CreateInstance();
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true);
|
||||
}
|
||||
|
||||
if (!RulesetContainer.Objects.Any())
|
||||
throw new InvalidOperationException("Beatmap contains no hit objects!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, "Could not load beatmap sucessfully!");
|
||||
|
||||
//couldn't load, hard abort!
|
||||
Exit();
|
||||
return;
|
||||
}
|
||||
|
||||
sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
|
||||
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
|
||||
var firstObjectTime = RulesetContainer.Objects.First().StartTime;
|
||||
adjustableClock.Seek(AllowLeadIn
|
||||
? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))
|
||||
: firstObjectTime);
|
||||
|
||||
adjustableClock.ProcessFrame();
|
||||
|
||||
// the final usable gameplay clock with user-set offsets applied.
|
||||
var offsetClock = new FramedOffsetClock(adjustableClock);
|
||||
|
||||
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
|
||||
userAudioOffset.TriggerChange();
|
||||
|
||||
scoreProcessor = RulesetContainer.CreateScoreProcessor();
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = offsetClock,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
storyboardContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
},
|
||||
RulesetContainer,
|
||||
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
|
||||
{
|
||||
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
|
||||
ProcessCustomClock = false,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
ProcessCustomClock = false,
|
||||
Breaks = beatmap.Breaks
|
||||
},
|
||||
scoreboard = new MultiplayerScoreboard(RulesetNetworkingClientHandler, playerList, scoreProcessor)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (showStoryboard)
|
||||
initializeStoryboard(false);
|
||||
|
||||
// Bind ScoreProcessor to ourselves
|
||||
scoreProcessor.AllJudged += onCompletion;
|
||||
scoreProcessor.Failed += onFail;
|
||||
|
||||
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToScoreProcessor>())
|
||||
mod.ApplyToScoreProcessor(scoreProcessor);
|
||||
}
|
||||
|
||||
private void applyRateFromMods()
|
||||
{
|
||||
if (sourceClock == null) return;
|
||||
|
||||
sourceClock.Rate = 1;
|
||||
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToClock>())
|
||||
mod.ApplyToClock(sourceClock);
|
||||
}
|
||||
|
||||
private void initializeStoryboard(bool asyncLoad)
|
||||
{
|
||||
if (storyboardContainer == null)
|
||||
return;
|
||||
|
||||
var beatmap = Beatmap.Value;
|
||||
|
||||
storyboard = beatmap.Storyboard.CreateDrawable();
|
||||
storyboard.Masking = true;
|
||||
|
||||
if (asyncLoad)
|
||||
LoadComponentAsync(storyboard, storyboardContainer.Add);
|
||||
else
|
||||
storyboardContainer.Add(storyboard);
|
||||
}
|
||||
|
||||
private ScheduledDelegate onCompletionEvent;
|
||||
|
||||
private void onCompletion()
|
||||
{
|
||||
// Only show the completion screen if the player hasn't failed
|
||||
if (scoreProcessor.HasFailed || onCompletionEvent != null)
|
||||
return;
|
||||
|
||||
ValidForResume = false;
|
||||
|
||||
if (!AllowResults) return;
|
||||
|
||||
using (BeginDelayedSequence(1000))
|
||||
{
|
||||
onCompletionEvent = Schedule(delegate
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
var score = new Score
|
||||
{
|
||||
Beatmap = Beatmap.Value.BeatmapInfo,
|
||||
Ruleset = ruleset
|
||||
};
|
||||
scoreProcessor.PopulateScore(score);
|
||||
score.User = RulesetContainer.Replay?.User ?? api.LocalUser.Value;
|
||||
Push(new Results(score));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool onFail()
|
||||
{
|
||||
if (Beatmap.Value.Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
|
||||
return false;
|
||||
|
||||
HasFailed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
|
||||
if (!loadedSuccessfully)
|
||||
return;
|
||||
|
||||
Content.Alpha = 0;
|
||||
Content
|
||||
.ScaleTo(0.7f)
|
||||
.ScaleTo(1, 750, Easing.OutQuint)
|
||||
.Delay(250)
|
||||
.FadeIn(250);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
sourceClock.Reset();
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
adjustableClock.ChangeSource(sourceClock);
|
||||
applyRateFromMods();
|
||||
|
||||
this.Delay(750).Schedule(() =>
|
||||
{
|
||||
Logger.Log("Client finnished loading", LoggingTarget.Network, LogLevel.Verbose);
|
||||
RulesetNetworkingClientHandler.GameLoaded();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void start()
|
||||
{
|
||||
adjustableClock.Start();
|
||||
}
|
||||
|
||||
protected override void OnSuspending(Screen next)
|
||||
{
|
||||
fadeOut();
|
||||
base.OnSuspending(next);
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
fadeOut();
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected override void UpdateBackgroundElements()
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
base.UpdateBackgroundElements();
|
||||
|
||||
if (ShowStoryboard && storyboard == null)
|
||||
initializeStoryboard(true);
|
||||
|
||||
var beatmap = Beatmap.Value;
|
||||
var storyboardVisible = ShowStoryboard && beatmap.Storyboard.HasDrawable;
|
||||
|
||||
storyboardContainer?
|
||||
.FadeColour(OsuColour.Gray(BackgroundOpacity), BACKGROUND_FADE_DURATION, Easing.OutQuint)
|
||||
.FadeTo(storyboardVisible && BackgroundOpacity > 0 ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
|
||||
|
||||
if (storyboardVisible && beatmap.Storyboard.ReplacesBackground)
|
||||
Background?.FadeTo(0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void fadeOut()
|
||||
{
|
||||
const float fade_out_duration = 250;
|
||||
|
||||
RulesetContainer?.FadeOut(fade_out_duration);
|
||||
Content.FadeOut(fade_out_duration);
|
||||
|
||||
hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, Easing.In);
|
||||
|
||||
Background?.FadeTo(1f, fade_out_duration);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (args.Key == Key.Escape)
|
||||
BackOut();
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
|
||||
public void BackOut()
|
||||
{
|
||||
RulesetNetworkingClientHandler.AbortGame();
|
||||
RulesetNetworkingClientHandler.OnAbort();
|
||||
}
|
||||
|
||||
protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Screens;
|
||||
using System;
|
||||
using osu.Framework.Screens;
|
||||
using System.Collections.Generic;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public abstract class RulesetLobbyScreen : OsuScreen
|
||||
{
|
||||
public abstract string RulesetName { get; }
|
||||
|
||||
public abstract RulesetMatchScreen MatchScreen { get; }
|
||||
|
||||
public RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
public readonly SettingsButton HostGameButton;
|
||||
public readonly SettingsButton DirectConnectButton;
|
||||
public readonly SettingsButton JoinGameButton;
|
||||
|
||||
public readonly Container NewGame;
|
||||
protected readonly TextBox HostIP;
|
||||
protected readonly TextBox HostPort;
|
||||
//protected readonly TextBox PublicIp;
|
||||
protected readonly TextBox LocalIp;
|
||||
|
||||
public readonly Container JoinIP;
|
||||
|
||||
public RulesetLobbyScreen()
|
||||
{
|
||||
AlwaysPresent = true;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
HostGameButton = new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Host Game",
|
||||
Action = HostGame
|
||||
},
|
||||
DirectConnectButton = new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Direct Connect",
|
||||
Action = DirectConnect
|
||||
},
|
||||
JoinGameButton = new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Join Game"
|
||||
},
|
||||
NewGame = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Size = new Vector2(400, 300),
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Blue,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
HostIP = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "Host IP Address"
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
HostPort = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "25570"
|
||||
},
|
||||
/*
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 22),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
PublicIp = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 22),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "You're Public IP Address"
|
||||
},
|
||||
*/
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 44),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
LocalIp = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 44),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "You're Local IP Address"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
MakeCurrent();
|
||||
}
|
||||
|
||||
protected override void OnResuming(Screen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
MakeCurrent();
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
}
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected virtual void HostGame()
|
||||
{
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
}
|
||||
Add(RulesetNetworkingClientHandler = new RulesetNetworkingClientHandler(ClientType.Host, LocalIp.Text, Int32.Parse(HostPort.Text)));
|
||||
|
||||
List<ClientInfo> list = new List<ClientInfo>();
|
||||
list.Add(RulesetNetworkingClientHandler.RulesetClientInfo);
|
||||
|
||||
JoinMatch(list);
|
||||
}
|
||||
|
||||
protected virtual void DirectConnect()
|
||||
{
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
}
|
||||
Add(RulesetNetworkingClientHandler = new RulesetNetworkingClientHandler(ClientType.Peer, HostIP.Text, Int32.Parse(HostPort.Text), LocalIp.Text));
|
||||
|
||||
RulesetNetworkingClientHandler.OnConnectedToHost += (p) => JoinMatch(p);
|
||||
}
|
||||
|
||||
protected virtual void JoinMatch(List<ClientInfo> clientInfos)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
MakeCurrent();
|
||||
Push(MatchScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Screens;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Pieces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public abstract class RulesetMatchScreen : OsuScreen
|
||||
{
|
||||
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
private readonly MatchPlayerList playerList;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
protected MatchTools MatchTools;
|
||||
|
||||
private readonly Chat chat;
|
||||
|
||||
public RulesetMatchScreen(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.35f,
|
||||
Text = "Leave",
|
||||
Action = () => Exit()
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Open Song Select",
|
||||
Action = () => openSongSelect()
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.35f,
|
||||
Text = "Start Match",
|
||||
Action = () => RulesetNetworkingClientHandler.StartLoadingGame()
|
||||
},
|
||||
playerList = new MatchPlayerList(RulesetNetworkingClientHandler),
|
||||
MatchTools = new MatchTools(),
|
||||
chat = new Chat(RulesetNetworkingClientHandler)
|
||||
};
|
||||
|
||||
RulesetNetworkingClientHandler.OnPacketReceive += (Packet packet) =>
|
||||
{
|
||||
if (packet is RulesetPacket rulesetPacket && rulesetPacket.OnlineBeatmapID != -1)
|
||||
foreach (BeatmapSetInfo beatmapSet in beatmaps.GetAllUsableBeatmapSets())
|
||||
if (beatmapSet.OnlineBeatmapSetID == rulesetPacket.OnlineBeatmapSetID)
|
||||
{
|
||||
foreach (BeatmapInfo beatmap in beatmapSet.Beatmaps)
|
||||
if (beatmap.OnlineBeatmapID == rulesetPacket.OnlineBeatmapID)
|
||||
{
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
|
||||
Beatmap.Value.Track.Start();
|
||||
MatchTools.MapChange(Beatmap);
|
||||
RulesetNetworkingClientHandler.OnMapChange?.Invoke(Beatmap);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
MatchTools.MapChange(rulesetPacket.OnlineBeatmapSetID);
|
||||
};
|
||||
|
||||
RulesetNetworkingClientHandler.OnMapChange += (beatmap) => MatchTools.MapChange(beatmap);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
playerList.Add(RulesetNetworkingClientHandler.RulesetClientInfo);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps)
|
||||
{
|
||||
this.beatmaps = beatmaps;
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
MakeCurrent();
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.OnLoadGame = (i) => Load(i);
|
||||
}
|
||||
|
||||
protected override void OnResuming(Screen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
MakeCurrent();
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
}
|
||||
|
||||
protected override void OnSuspending(Screen next)
|
||||
{
|
||||
base.OnSuspending(next);
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
RulesetNetworkingClientHandler.Disconnect();
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected virtual void Load(List<ClientInfo> playerList)
|
||||
{
|
||||
MakeCurrent();
|
||||
Push(new MultiPlayer(RulesetNetworkingClientHandler, playerList));
|
||||
}
|
||||
|
||||
private void openSongSelect()
|
||||
{
|
||||
MatchSongSelect songSelect = new MatchSongSelect(RulesetNetworkingClientHandler);
|
||||
MakeCurrent();
|
||||
Push(songSelect);
|
||||
songSelect.Action = () => RulesetNetworkingClientHandler.SetMap(songSelect.SelectedMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//osu.Game.Screens.Symcol.SymcolMenu
|
||||
//Symcol.Rulesets.Core.SymcolSettingsSubsection
|
||||
#define SymcolMods
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Symcol;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public class RulesetMultiplayerSelection : OsuScreen
|
||||
{
|
||||
public static readonly FillFlowContainer<RulesetLobbyItem> LobbyItems = new FillFlowContainer<RulesetLobbyItem>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.85f,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
};
|
||||
|
||||
public RulesetMultiplayerSelection()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Add(LobbyItems);
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
foreach (RulesetLobbyItem item in LobbyItems)
|
||||
item.Action = () => Push(item.RulesetLobbyScreen);
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
Remove(LobbyItems);
|
||||
|
||||
#if SymcolMods
|
||||
SymcolSettingsSubsection.RulesetMultiplayerSelection = new RulesetMultiplayerSelection();
|
||||
|
||||
SymcolMenu.RulesetMultiplayerScreen = SymcolSettingsSubsection.RulesetMultiplayerSelection;
|
||||
#endif
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class RulesetLobbyItem : ClickableContainer
|
||||
{
|
||||
public abstract Texture Icon { get; }
|
||||
|
||||
public abstract string RulesetName { get; }
|
||||
|
||||
public virtual Texture Background { get; }
|
||||
|
||||
public abstract RulesetLobbyScreen RulesetLobbyScreen { get; }
|
||||
|
||||
public RulesetLobbyItem()
|
||||
{
|
||||
CornerRadius = 20;
|
||||
Masking = true;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 100;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = Background
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new Sprite
|
||||
{
|
||||
Size = new Vector2(Height),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Texture = Icon
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Text = RulesetName,
|
||||
TextSize = 60,
|
||||
Position = new Vector2(-20, 0)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Symcol.Rulesets.Core")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Symcol.Rulesets.Core")]
|
||||
[assembly: AssemblyCopyright("Copyright © Shawdooow 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("552b5940-c647-4060-aa4d-61baac415c72")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,97 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Skinning
|
||||
{
|
||||
public abstract class SkinElement : Container
|
||||
{
|
||||
private static string loadedSkin;
|
||||
|
||||
private static ResourceStore<byte[]> skinResources;
|
||||
private static TextureStore skinTextures;
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to get a skin element fron the skin, if no element is found return the default element
|
||||
/// </summary>
|
||||
/// <param name="stockTextures"></param>
|
||||
/// <param name="skin"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="storage"></param>
|
||||
/// <returns></returns>
|
||||
public static Texture GetSkinElement(TextureStore stockTextures, Bindable<string> skin, string fileName, Storage storage)
|
||||
{
|
||||
Texture texture = null;
|
||||
|
||||
string fileNameHd = fileName + "@2x";
|
||||
|
||||
Storage skinStorage = storage.GetStorageForDirectory("Skins\\" + skin);
|
||||
|
||||
if (skin.Value == "default")
|
||||
{
|
||||
texture = stockTextures.Get(fileName + ".png");
|
||||
|
||||
if (texture == null)
|
||||
texture = stockTextures.Get(fileNameHd + ".png");
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (loadedSkin != skin.ToString())
|
||||
{
|
||||
loadedSkin = skin.ToString();
|
||||
skinResources = new ResourceStore<byte[]>(new StorageBackedResourceStore(skinStorage));
|
||||
skinTextures = new TextureStore(new RawTextureLoaderStore(skinResources));
|
||||
}
|
||||
|
||||
if (skinStorage.Exists(fileNameHd + ".png"))
|
||||
texture = skinTextures.Get(fileNameHd + ".png");
|
||||
else if (skinStorage.Exists(fileName + ".png"))
|
||||
{
|
||||
texture = skinTextures.Get(fileName + ".png");
|
||||
texture.ScaleAdjust = 1f;
|
||||
}
|
||||
else
|
||||
texture = stockTextures.Get(fileNameHd + ".png");
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to get a skin element from the skin, if no element is found return null
|
||||
/// </summary>
|
||||
/// <param name="skin"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="storage"></param>
|
||||
/// <returns></returns>
|
||||
public static Texture GetElement(Bindable<string> skin, string fileName, Storage storage)
|
||||
{
|
||||
Texture texture = null;
|
||||
|
||||
string fileNameHd = fileName + "@2x";
|
||||
|
||||
Storage skinStorage = storage.GetStorageForDirectory("Skins\\" + skin);
|
||||
|
||||
if (loadedSkin != skin.ToString())
|
||||
{
|
||||
loadedSkin = skin.ToString();
|
||||
skinResources = new ResourceStore<byte[]>(new StorageBackedResourceStore(skinStorage));
|
||||
skinTextures = new TextureStore(new RawTextureLoaderStore(skinResources));
|
||||
}
|
||||
|
||||
if (skinStorage.Exists(fileNameHd + ".png"))
|
||||
texture = skinTextures.Get(fileNameHd + ".png");
|
||||
else if (skinStorage.Exists(fileName + ".png"))
|
||||
{
|
||||
texture = skinTextures.Get(fileName + ".png");
|
||||
texture.ScaleAdjust = 1f;
|
||||
}
|
||||
else
|
||||
texture = null;
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Skinning
|
||||
{
|
||||
public class SkinConfigReader<T> : IniConfigManager<T>
|
||||
where T : struct
|
||||
{
|
||||
protected override string Filename => @"skin.ini";
|
||||
|
||||
public SkinConfigReader(Storage storage) : base(storage) { }
|
||||
|
||||
protected override bool PerformSave() { return false; }
|
||||
}
|
||||
|
||||
//wildly incomplete
|
||||
public enum ClassicIniParameters
|
||||
{
|
||||
Name,
|
||||
Author,
|
||||
CursorRotate,
|
||||
CursorExpand,
|
||||
CursorCentre
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{552B5940-C647-4060-AA4D-61BAAC415C72}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Symcol.Rulesets.Core</RootNamespace>
|
||||
<AssemblyName>Symcol.Rulesets.Core</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Containers\LinkText.cs" />
|
||||
<Compile Include="Containers\ProfileLink.cs" />
|
||||
<Compile Include="HitObjects\DrawableSymcolHitObject.cs" />
|
||||
<Compile Include="Multiplayer\Networking\ScorePacket.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\Chat.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\ChatMessage.cs" />
|
||||
<Compile Include="Multiplayer\Networking\ChatPacket.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MatchTools.cs" />
|
||||
<Compile Include="Multiplayer\Networking\RulesetClientInfo.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MatchPlayer.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MatchPlayerList.cs" />
|
||||
<Compile Include="Multiplayer\Options\MultiplayerDropdownEnumOption.cs" />
|
||||
<Compile Include="Multiplayer\Options\MultiplayerOption.cs" />
|
||||
<Compile Include="Multiplayer\Options\MultiplayerToggleOption.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MultiplayerScoreboard.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MultiplayerScoreboardItem.cs" />
|
||||
<Compile Include="Multiplayer\Screens\MatchSongSelect.cs" />
|
||||
<Compile Include="Multiplayer\Screens\MultiPlayer.cs" />
|
||||
<Compile Include="Multiplayer\Screens\RulesetLobbyScreen.cs" />
|
||||
<Compile Include="Multiplayer\Screens\RulesetMatchScreen.cs" />
|
||||
<Compile Include="Multiplayer\Screens\RulesetMultiplayerSelection.cs" />
|
||||
<Compile Include="Multiplayer\Networking\RulesetNetworkingClientHandler.cs" />
|
||||
<Compile Include="Multiplayer\Networking\RulesetPacket.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Skinning\SkinElement.cs" />
|
||||
<Compile Include="Skinning\SkinIniReader.cs" />
|
||||
<Compile Include="SymcolConfigManager.cs" />
|
||||
<Compile Include="SymcolPlayfield.cs" />
|
||||
<Compile Include="SymcolSettingsSubsection.cs" />
|
||||
<Compile Include="SymcolInputManager.cs" />
|
||||
<Compile Include="VectorVideos\VectorVideo.cs" />
|
||||
<Compile Include="Wiki\WikiOptionEnumExplanation.cs" />
|
||||
<Compile Include="Wiki\WikiParagraph.cs" />
|
||||
<Compile Include="Wiki\WikiSubSectionHeader.cs" />
|
||||
<Compile Include="Wiki\WikiOverlay.cs" />
|
||||
<Compile Include="Wiki\WikiHeader.cs" />
|
||||
<Compile Include="Wiki\WikiSection.cs" />
|
||||
<Compile Include="Wiki\WikiSubSectionLinkHeader.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-Framework\osu.Framework\osu.Framework.csproj">
|
||||
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
|
||||
<Name>osu.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<Project>{d9a367c9-4c1a-489f-9b05-a0cea2b53b58}</Project>
|
||||
<Name>osu.Game.Resources</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
|
||||
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
|
||||
<Name>osu.Game</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Symcol.Core\Symcol.Core.csproj">
|
||||
<Project>{F34AC16C-E590-4D70-A069-A748326852BF}</Project>
|
||||
<Name>Symcol.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,22 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public class SymcolConfigManager : IniConfigManager<SymcolSetting>
|
||||
{
|
||||
protected override string Filename => "symcol.ini";
|
||||
|
||||
public SymcolConfigManager(Storage storage) : base(storage) { }
|
||||
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
Set(SymcolSetting.PlayerColor, "#ffffff");
|
||||
}
|
||||
}
|
||||
|
||||
public enum SymcolSetting
|
||||
{
|
||||
PlayerColor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using Symcol.Rulesets.Core.VectorVideos;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public class SymcolInputManager<T> : RulesetInputManager<T>
|
||||
where T : struct
|
||||
{
|
||||
protected virtual bool VectorVideo => false;
|
||||
|
||||
public SymcolInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
|
||||
{
|
||||
Child = new VectorVideo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public class SymcolPlayfield : Playfield
|
||||
{
|
||||
public static RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
public SymcolPlayfield(Vector2 size) : base(size.X)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//osu.Game.Screens.Symcol.SymcolMenu
|
||||
//Symcol.Rulesets.Core.Multiplayer.Screens.RulesetMultiplayerSelection
|
||||
#define SymcolMods
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using Symcol.Rulesets.Core.Wiki;
|
||||
using osu.Game.Screens.Symcol;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Screens;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Logging;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public abstract class SymcolSettingsSubsection : SettingsSubsection
|
||||
{
|
||||
public virtual WikiOverlay Wiki => null;
|
||||
|
||||
public virtual RulesetLobbyItem RulesetLobbyItem => null;
|
||||
|
||||
#if SymcolMods
|
||||
public static RulesetMultiplayerSelection RulesetMultiplayerSelection;
|
||||
#endif
|
||||
|
||||
public static SymcolConfigManager SymcolConfigManager;
|
||||
|
||||
private OsuGame osu;
|
||||
|
||||
public SymcolSettingsSubsection()
|
||||
{
|
||||
|
||||
#if SymcolMods
|
||||
if (RulesetLobbyItem != null)
|
||||
RulesetMultiplayerSelection.LobbyItems.Add(RulesetLobbyItem);
|
||||
|
||||
if (RulesetMultiplayerSelection == null)
|
||||
RulesetMultiplayerSelection = new RulesetMultiplayerSelection();
|
||||
|
||||
SymcolMenu.RulesetMultiplayerScreen = RulesetMultiplayerSelection;
|
||||
#endif
|
||||
#if !SymcolMods
|
||||
Logger.Log("osu.Game mods not installed! Online Multiplayer will not be avalible without them. . .", LoggingTarget.Information, LogLevel.Important);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGame osu, Storage storage)
|
||||
{
|
||||
this.osu = osu;
|
||||
|
||||
if (SymcolConfigManager == null)
|
||||
SymcolConfigManager = new SymcolConfigManager(storage);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (Wiki != null)
|
||||
osu.Add(Wiki);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.VectorVideos
|
||||
{
|
||||
public class VectorVideo : BeatSyncedContainer
|
||||
{
|
||||
public const string FILE_NAME = "VectorVideo.symcol";
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Storage storage)
|
||||
{
|
||||
}
|
||||
|
||||
protected void LoadContent(string args)
|
||||
{
|
||||
string[] parameters = args.Split(',');
|
||||
|
||||
ObjectType objectType = ObjectType.LogoVisualizer;
|
||||
Anchor anchor = Anchor.Centre;
|
||||
Anchor origin = Anchor.Centre;
|
||||
|
||||
bool checkingType = false;
|
||||
|
||||
foreach (string parameter in parameters)
|
||||
{
|
||||
string[] subParameters = parameter.Split('=');
|
||||
|
||||
foreach (string subParameter in subParameters)
|
||||
{
|
||||
if (subParameter == "Type")
|
||||
checkingType = true;
|
||||
|
||||
if (checkingType)
|
||||
switch (subParameter)
|
||||
{
|
||||
case "LogoVisualizer":
|
||||
objectType = ObjectType.LogoVisualizer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLogoVisualizer()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum ObjectType
|
||||
{
|
||||
LogoVisualizer
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using OpenTK.Graphics;
|
||||
using Symcol.Rulesets.Core.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public abstract class WikiHeader : Container
|
||||
{
|
||||
protected abstract Texture RulesetIcon { get; }
|
||||
|
||||
protected abstract string RulesetName { get; }
|
||||
|
||||
protected abstract string RulesetDescription { get; }
|
||||
|
||||
protected virtual string RulesetUrl => $@"https://osu.ppy.sh/home";
|
||||
|
||||
protected virtual User Creator => null;
|
||||
|
||||
protected virtual User Maintainer => null;
|
||||
|
||||
protected virtual string DiscordInvite => $@"https://discord.gg/ppy";
|
||||
|
||||
protected virtual Texture HeaderBackground => null;
|
||||
|
||||
private const float description_height = 150;
|
||||
private const float description_width = 220;
|
||||
private const float icon_size = 200;
|
||||
private const float header_margin = 50;
|
||||
private const float rulesetname_height = 60;
|
||||
|
||||
public WikiHeader()
|
||||
{
|
||||
Masking = true;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = header_margin + icon_size + rulesetname_height;
|
||||
|
||||
|
||||
var user = Creator;
|
||||
bool maintainer = false;
|
||||
string userTitle = "Creator";
|
||||
if (Creator == null)
|
||||
{
|
||||
user = Maintainer;
|
||||
maintainer = true;
|
||||
userTitle = "Maintainer";
|
||||
}
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = HeaderBackground
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new Sprite
|
||||
{
|
||||
Size = new Vector2(icon_size),
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Texture = RulesetIcon
|
||||
},
|
||||
new LinkText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, icon_size),
|
||||
Text = RulesetName,
|
||||
Url = RulesetUrl,
|
||||
Font = @"Exo2.0-RegularItalic",
|
||||
TextSize = rulesetname_height
|
||||
},
|
||||
new ProfileLink(user, maintainer)
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, icon_size + rulesetname_height),
|
||||
},
|
||||
new LinkText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, icon_size + rulesetname_height + 20),
|
||||
Text = userTitle + "'s Discord server",
|
||||
Url = DiscordInvite,
|
||||
Font = @"Exo2.0-RegularItalic",
|
||||
TextSize = 16
|
||||
},
|
||||
new OsuTextFlowContainer(t => { t.TextSize = 20; })
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = new Vector2(description_width, description_height),
|
||||
Text = RulesetDescription
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiOptionEnumExplanation<T> : Container
|
||||
where T : struct
|
||||
{
|
||||
public OsuTextFlowContainer Description;
|
||||
|
||||
public WikiOptionEnumExplanation(Bindable<T> bindable)
|
||||
{
|
||||
OsuColour osu = new OsuColour();
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = osu.Yellow,
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Size = new Vector2(10, 0.98f),
|
||||
CornerRadius = 5,
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Position = new Vector2(-10, 0),
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.45f,
|
||||
|
||||
Child = new SettingsEnumDropdown<T>
|
||||
{
|
||||
Bindable = bindable
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.45f,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
AutoSizeDuration = 100,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
|
||||
Child = Description = new OsuTextFlowContainer(t => { t.TextSize = 20; })
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using System.Linq;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public abstract class WikiOverlay : WaveOverlayContainer
|
||||
{
|
||||
protected abstract WikiHeader Header { get; }
|
||||
protected abstract WikiSection[] Sections { get; }
|
||||
|
||||
private WikiSection lastSection;
|
||||
private SectionsContainer<WikiSection> sectionsContainer;
|
||||
private WikiTabControl tabs;
|
||||
|
||||
public const float CONTENT_X_MARGIN = 100;
|
||||
|
||||
public WikiOverlay()
|
||||
{
|
||||
FirstWaveColour = OsuColour.Gray(0.4f);
|
||||
SecondWaveColour = OsuColour.Gray(0.3f);
|
||||
ThirdWaveColour = OsuColour.Gray(0.2f);
|
||||
FourthWaveColour = OsuColour.Gray(0.1f);
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
Width = 0.85f;
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
Masking = true;
|
||||
AlwaysPresent = true;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 10
|
||||
};
|
||||
|
||||
tabs = new WikiTabControl
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 30
|
||||
};
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f)
|
||||
});
|
||||
|
||||
Add(sectionsContainer = new SectionsContainer<WikiSection>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ExpandableHeader = Header,
|
||||
FixedHeader = tabs,
|
||||
HeaderBackground = new Box
|
||||
{
|
||||
Colour = OsuColour.Gray(34),
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
});
|
||||
|
||||
sectionsContainer.SelectedSection.ValueChanged += s =>
|
||||
{
|
||||
if (lastSection != s)
|
||||
{
|
||||
lastSection = s;
|
||||
tabs.Current.Value = lastSection;
|
||||
}
|
||||
};
|
||||
|
||||
tabs.Current.ValueChanged += s =>
|
||||
{
|
||||
if (lastSection == null)
|
||||
{
|
||||
lastSection = sectionsContainer.Children.FirstOrDefault();
|
||||
if (lastSection != null)
|
||||
tabs.Current.Value = lastSection;
|
||||
return;
|
||||
}
|
||||
if (lastSection != s)
|
||||
{
|
||||
lastSection = s;
|
||||
sectionsContainer.ScrollTo(lastSection);
|
||||
}
|
||||
};
|
||||
|
||||
foreach (WikiSection sec in Sections)
|
||||
{
|
||||
if (sec != null)
|
||||
{
|
||||
sectionsContainer.Add(sec);
|
||||
tabs.AddItem(sec);
|
||||
}
|
||||
}
|
||||
|
||||
sectionsContainer.ScrollToTop();
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
|
||||
}
|
||||
|
||||
private class WikiTabControl : PageTabControl<WikiSection>
|
||||
{
|
||||
public WikiTabControl()
|
||||
{
|
||||
TabContainer.RelativeSizeAxes &= ~Axes.X;
|
||||
TabContainer.AutoSizeAxes |= Axes.X;
|
||||
TabContainer.Anchor |= Anchor.x1;
|
||||
TabContainer.Origin |= Anchor.x1;
|
||||
}
|
||||
|
||||
protected override TabItem<WikiSection> CreateTabItem(WikiSection value) => new WikiTabItem(value);
|
||||
|
||||
protected override Dropdown<WikiSection> CreateDropdown() => null;
|
||||
|
||||
private class WikiTabItem : PageTabItem
|
||||
{
|
||||
public WikiTabItem(WikiSection value) : base(value)
|
||||
{
|
||||
Text.Text = value.Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiParagraph : Container
|
||||
{
|
||||
public WikiParagraph(string text, float textsize = 20)
|
||||
{
|
||||
paragraphNoMarkdown(text, textsize);
|
||||
}
|
||||
|
||||
public WikiParagraph(string text, bool markdown)
|
||||
{
|
||||
if (!markdown)
|
||||
paragraphNoMarkdown(text, 20);
|
||||
else
|
||||
paragraphMarkdown(text, 20);
|
||||
}
|
||||
public WikiParagraph(string text, float textsize, bool markdown)
|
||||
{
|
||||
if (!markdown)
|
||||
paragraphNoMarkdown(text, textsize);
|
||||
else
|
||||
paragraphMarkdown(text, textsize);
|
||||
}
|
||||
|
||||
private void paragraphNoMarkdown(string text, float textsize)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Child = new OsuTextFlowContainer(t => { t.TextSize = textsize; })
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = text
|
||||
};
|
||||
}
|
||||
|
||||
private void paragraphMarkdown(string text, float textsize)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Child = new OsuTextFlowContainer(t => { t.TextSize = textsize; })
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = text
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public abstract class WikiSection : FillFlowContainer
|
||||
{
|
||||
public abstract string Title { get; }
|
||||
|
||||
private readonly FillFlowContainer content;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
protected WikiSection()
|
||||
{
|
||||
OsuColour osu = new OsuColour();
|
||||
Direction = FillDirection.Vertical;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Colour = osu.Yellow,
|
||||
Text = Title,
|
||||
TextSize = 32,
|
||||
Font = @"Exo2.0-Bold",
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Horizontal = WikiOverlay.CONTENT_X_MARGIN,
|
||||
Vertical = 12
|
||||
}
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = WikiOverlay.CONTENT_X_MARGIN,
|
||||
Bottom = 20
|
||||
}
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 1,
|
||||
Colour = OsuColour.Gray(34),
|
||||
EdgeSmoothness = new Vector2(1)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiSubSectionHeader : OsuSpriteText
|
||||
{
|
||||
public WikiSubSectionHeader(string text)
|
||||
{
|
||||
OsuColour osu = new OsuColour();
|
||||
Colour = osu.Pink;
|
||||
Text = text;
|
||||
TextSize = 24;
|
||||
Font = @"Exo2.0-BoldItalic";
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Vertical = 10
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using Symcol.Rulesets.Core.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiSubSectionLinkHeader : LinkText
|
||||
{
|
||||
public override string Tooltip => tooltip;
|
||||
|
||||
private string tooltip = "";
|
||||
|
||||
public WikiSubSectionLinkHeader(string text, string url, string tooltip = "")
|
||||
{
|
||||
this.tooltip = tooltip;
|
||||
Url = url;
|
||||
OsuColour osu = new OsuColour();
|
||||
Colour = osu.Pink;
|
||||
Text = text;
|
||||
TextSize = 24;
|
||||
Font = @"Exo2.0-BoldItalic";
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Vertical = 10
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -12,7 +12,7 @@ install:
|
||||
- cmd: git submodule update --init --recursive --depth=5
|
||||
- cmd: choco install resharper-clt -y
|
||||
- cmd: choco install nvika -y
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe
|
||||
before_build:
|
||||
- cmd: CodeFileSanity.exe
|
||||
- cmd: nuget restore -verbosity quiet
|
||||
@@ -20,6 +20,10 @@ build:
|
||||
project: osu.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
test:
|
||||
assemblies:
|
||||
only:
|
||||
- 'osu.Desktop\**\*.dll'
|
||||
after_build:
|
||||
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
|
||||
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
||||
+1
-1
Submodule osu-framework updated: 8f36ddab94...d8d4f55e10
+1
-1
Submodule osu-resources updated: 7724abdf1d...6e145ed502
@@ -111,16 +111,11 @@ namespace osu.Desktop
|
||||
{
|
||||
var filePaths = new [] { e.FileName };
|
||||
|
||||
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
|
||||
Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning);
|
||||
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
|
||||
Task.Run(() =>
|
||||
{
|
||||
var score = ScoreStore.ReadReplayFile(filePaths.First());
|
||||
Schedule(() => LoadScore(score));
|
||||
});
|
||||
}
|
||||
var firstExtension = Path.GetExtension(filePaths.First());
|
||||
|
||||
private static readonly string[] allowed_extensions = { @".osz", @".osr" };
|
||||
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
||||
|
||||
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.IPC;
|
||||
@@ -15,6 +16,9 @@ namespace osu.Desktop
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (!RuntimeInfo.IsMono)
|
||||
useMulticoreJit();
|
||||
|
||||
// Back up the cwd before DesktopGameHost changes it
|
||||
var cwd = Environment.CurrentDirectory;
|
||||
|
||||
@@ -22,7 +26,7 @@ namespace osu.Desktop
|
||||
{
|
||||
if (!host.IsPrimaryInstance)
|
||||
{
|
||||
var importer = new BeatmapIPCChannel(host);
|
||||
var importer = new ArchiveImportIPCChannel(host);
|
||||
// Restore the cwd so relative paths given at the command line work correctly
|
||||
Directory.SetCurrentDirectory(cwd);
|
||||
foreach (var file in args)
|
||||
@@ -44,8 +48,16 @@ namespace osu.Desktop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void useMulticoreJit()
|
||||
{
|
||||
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
|
||||
ProfileOptimization.SetProfileRoot(directory.FullName);
|
||||
ProfileOptimization.StartProfile("Startup.Profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
|
||||
@@ -237,10 +237,18 @@
|
||||
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
|
||||
<Name>osu.Game.Rulesets.Osu</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Shape\osu.Game.Rulesets.Shape.csproj">
|
||||
<Project>{baf6d7b6-5e48-4278-ab81-f079af640e31}</Project>
|
||||
<Name>osu.Game.Rulesets.Shape</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
|
||||
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
|
||||
<Name>osu.Game.Rulesets.Taiko</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Vitaru\osu.Game.Rulesets.Vitaru.csproj">
|
||||
<Project>{9a615027-b2ab-435e-b865-0c7209933457}</Project>
|
||||
<Name>osu.Game.Rulesets.Vitaru</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game.Tests\osu.Game.Tests.csproj">
|
||||
<Project>{54377672-20b1-40af-8087-5cf73bf3953a}</Project>
|
||||
<Name>osu.Game.Tests</Name>
|
||||
@@ -249,6 +257,14 @@
|
||||
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
|
||||
<Name>osu.Game</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Symcol.Core\Symcol.Core.csproj">
|
||||
<Project>{f34ac16c-e590-4d70-a069-a748326852bf}</Project>
|
||||
<Name>Symcol.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Symcol.Rulesets.Core\Symcol.Rulesets.Core.csproj">
|
||||
<Project>{552b5940-c647-4060-aa4d-61baac415c72}</Project>
|
||||
<Name>Symcol.Rulesets.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
||||
@@ -12,15 +12,13 @@
|
||||
<description>click the circles. to the beat.</description>
|
||||
<summary>click the circles.</summary>
|
||||
<releaseNotes>testing</releaseNotes>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
|
||||
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
|
||||
<language>en-AU</language>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||
<file src="*.dll" target="lib\net45\"/>
|
||||
<file src="*.config" target="lib\net45\"/>
|
||||
<file src="x86\*.dll" target="lib\net45\x86\"/>
|
||||
<file src="x64\*.dll" target="lib\net45\x64\"/>
|
||||
<file src="**.exe" target="lib\net45\" exclude="**vshost**"/>
|
||||
<file src="**.dll" target="lib\net45\"/>
|
||||
<file src="**.config" target="lib\net45\"/>
|
||||
</files>
|
||||
</package>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<packages>
|
||||
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
|
||||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
|
||||
<package id="Splat" version="2.0.0" targetFramework="net45" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
|
||||
@@ -16,29 +16,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
||||
{
|
||||
if (beatmap.ComboColors.Count == 0)
|
||||
return;
|
||||
|
||||
int index = 0;
|
||||
int colourIndex = 0;
|
||||
|
||||
CatchHitObject lastObj = null;
|
||||
|
||||
initialiseHyperDash(beatmap.HitObjects);
|
||||
|
||||
base.PostProcess(beatmap);
|
||||
|
||||
int index = 0;
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
if (obj.NewCombo)
|
||||
{
|
||||
if (lastObj != null) lastObj.LastInCombo = true;
|
||||
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
|
||||
}
|
||||
|
||||
obj.IndexInBeatmap = index++;
|
||||
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
||||
|
||||
lastObj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||
|
||||
@@ -23,6 +23,5 @@ namespace osu.Game.Rulesets.Catch
|
||||
MoveRight,
|
||||
[Description("Engage dash")]
|
||||
Dash,
|
||||
PositionUpdate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ using osu.Game.Rulesets.UI;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
@@ -99,7 +101,9 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
|
||||
|
||||
public override int LegacyID => 2;
|
||||
public override int? LegacyID => 2;
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||
|
||||
public CatchRuleset(RulesetInfo rulesetInfo = null)
|
||||
: base(rulesetInfo)
|
||||
|
||||
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModDaycore : ModDaycore
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModEasy : ModEasy
|
||||
{
|
||||
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModHalfTime : ModHalfTime
|
||||
{
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override double ScoreMultiplier => 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
public class CatchModHardRock : ModHardRock
|
||||
{
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
public override bool Ranked => true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModHidden : ModHidden
|
||||
{
|
||||
public override string Description => @"Play with fading notes for a slight score advantage.";
|
||||
public override string Description => @"Play with fading fruits.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
@@ -32,25 +31,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
AddNested(new Banana
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = getNextComboColour(),
|
||||
StartTime = i,
|
||||
X = RNG.NextSingle()
|
||||
});
|
||||
}
|
||||
|
||||
private Color4 getNextComboColour()
|
||||
{
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
default:
|
||||
return new Color4(255, 240, 0, 255);
|
||||
case 1:
|
||||
return new Color4(255, 192, 0, 255);
|
||||
case 2:
|
||||
return new Color4(214, 221, 28, 255);
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
public double Duration { get; set; }
|
||||
|
||||
@@ -5,24 +5,25 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo
|
||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
|
||||
{
|
||||
public const double OBJECT_RADIUS = 44;
|
||||
|
||||
public float X { get; set; }
|
||||
|
||||
public Color4 ComboColour { get; set; }
|
||||
|
||||
public int IndexInBeatmap { get; set; }
|
||||
|
||||
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
||||
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
public int IndexInCurrentCombo { get; set; }
|
||||
|
||||
public int ComboIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The next fruit starts a new combo. Used for explodey.
|
||||
/// </summary>
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Origin = Anchor.BottomLeft;
|
||||
X = 0;
|
||||
|
||||
Child = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
|
||||
AddNested(getVisualRepresentation?.Invoke(b));
|
||||
|
||||
@@ -8,6 +8,8 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
@@ -53,10 +55,18 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
if (CheckPosition == null) return;
|
||||
|
||||
if (timeOffset > 0)
|
||||
if (timeOffset >= 0)
|
||||
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
if (HitObject is IHasComboInformation combo)
|
||||
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||
}
|
||||
|
||||
private const float preempt = 1000;
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
|
||||
@@ -5,28 +5,39 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
|
||||
{
|
||||
private Pulp pulp;
|
||||
|
||||
public DrawableDroplet(Droplet h)
|
||||
: base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
|
||||
AccentColour = h.ComboColour;
|
||||
Masking = false;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = new Pulp
|
||||
InternalChild = pulp = new Pulp
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
Size = Size
|
||||
};
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
pulp.AccentColour = AccentColour;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
|
||||
AccentColour = HitObject.ComboColour;
|
||||
Masking = false;
|
||||
|
||||
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||
@@ -33,7 +32,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new[]
|
||||
// todo: this should come from the skin.
|
||||
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
createPulp(HitObject.VisualRepresentation),
|
||||
border = new Circle
|
||||
@@ -65,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
|
||||
if (HitObject.HyperDash)
|
||||
{
|
||||
Add(new Pulp
|
||||
AddInternal(new Pulp
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
@@ -273,5 +275,31 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
|
||||
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
||||
}
|
||||
|
||||
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
||||
{
|
||||
switch (representation)
|
||||
{
|
||||
default:
|
||||
case FruitVisualRepresentation.Pear:
|
||||
return new Color4(17, 136, 170, 255);
|
||||
case FruitVisualRepresentation.Grape:
|
||||
return new Color4(204, 102, 0, 255);
|
||||
case FruitVisualRepresentation.Raspberry:
|
||||
return new Color4(121, 9, 13, 255);
|
||||
case FruitVisualRepresentation.Pineapple:
|
||||
return new Color4(102, 136, 0, 255);
|
||||
case FruitVisualRepresentation.Banana:
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
default:
|
||||
return new Color4(255, 240, 0, 255);
|
||||
case 1:
|
||||
return new Color4(255, 192, 0, 255);
|
||||
case 2:
|
||||
return new Color4(214, 221, 28, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,18 +20,19 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Origin = Anchor.BottomLeft;
|
||||
X = 0;
|
||||
|
||||
Child = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
|
||||
InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
|
||||
|
||||
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddNested(getVisualRepresentation?.Invoke(o));
|
||||
}
|
||||
|
||||
protected override bool ProvidesJudgement => false;
|
||||
|
||||
protected override void AddNested(DrawableHitObject h)
|
||||
{
|
||||
var catchObject = (DrawableCatchHitObject)h;
|
||||
|
||||
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||
catchObject.AccentColour = HitObject.ComboColour;
|
||||
|
||||
dropletContainer.Add(h);
|
||||
base.AddNested(h);
|
||||
|
||||
@@ -21,9 +21,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
private const float base_scoring_distance = 100;
|
||||
|
||||
public readonly SliderCurve Curve = new SliderCurve();
|
||||
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
public int RepeatCount { get; set; }
|
||||
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
@@ -55,81 +53,82 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
var spanDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
AddNested(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = StartTime,
|
||||
X = X
|
||||
});
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
double lastDropletTime = StartTime;
|
||||
|
||||
for (int span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
var spanStartTime = StartTime + span * spanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
for (double d = 0; d <= length; d += tickDistance)
|
||||
{
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
|
||||
AddNested(new Droplet
|
||||
double time = spanStartTime + timeProgress * spanDuration;
|
||||
|
||||
double tinyTickInterval = time - lastDropletTime;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
StartTime = t,
|
||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
double tinyTickInterval = tickDistance / length * repeatDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < repeatDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
|
||||
{
|
||||
StartTime = repeatStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
AddNested(new Droplet
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
StartTime = time,
|
||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
lastDropletTime = time;
|
||||
}
|
||||
|
||||
AddNested(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = repeatStartTime + repeatDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
StartTime = spanStartTime + spanDuration,
|
||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||
|
||||
public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
@@ -139,6 +138,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
public SliderCurve Curve { get; } = new SliderCurve();
|
||||
|
||||
public List<Vector2> ControlPoints
|
||||
{
|
||||
get { return Curve.ControlPoints; }
|
||||
@@ -152,17 +153,5 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
get { return Curve.CurveType; }
|
||||
set { Curve.CurveType = value; }
|
||||
}
|
||||
|
||||
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
|
||||
|
||||
public double ProgressAt(double progress)
|
||||
{
|
||||
double p = progress * RepeatCount % 1;
|
||||
if (RepeatAt(progress) % 2 == 1)
|
||||
p = 1 - p;
|
||||
return p;
|
||||
}
|
||||
|
||||
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// 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.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Users;
|
||||
|
||||
@@ -23,15 +26,78 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
|
||||
public override Replay Generate()
|
||||
{
|
||||
// todo: add support for HT DT
|
||||
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
|
||||
const double movement_speed = dash_speed / 2;
|
||||
float lastPosition = 0.5f;
|
||||
double lastTime = 0;
|
||||
|
||||
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
|
||||
Replay.Frames.Add(new CatchReplayFrame(-100000, 0));
|
||||
Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition));
|
||||
|
||||
void moveToNext(CatchHitObject h)
|
||||
{
|
||||
float positionChange = Math.Abs(lastPosition - h.X);
|
||||
double timeAvailable = h.StartTime - lastTime;
|
||||
|
||||
//So we can either make it there without a dash or not.
|
||||
double speedRequired = positionChange / timeAvailable;
|
||||
|
||||
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
|
||||
|
||||
// todo: get correct catcher size, based on difficulty CS.
|
||||
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
|
||||
|
||||
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
|
||||
{
|
||||
//we are already in the correct range.
|
||||
lastTime = h.StartTime;
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
|
||||
return;
|
||||
}
|
||||
|
||||
if (h is BananaShower.Banana)
|
||||
{
|
||||
// auto bananas unrealistically warp to catch 100% combo.
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else if (h.HyperDash)
|
||||
{
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else if (dashRequired)
|
||||
{
|
||||
//we do a movement in two parts - the dash part then the normal part...
|
||||
double timeAtNormalSpeed = positionChange / movement_speed;
|
||||
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
||||
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
||||
|
||||
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
||||
|
||||
//dash movement
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
else
|
||||
{
|
||||
double timeBefore = positionChange / movement_speed;
|
||||
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition));
|
||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||
}
|
||||
|
||||
lastTime = h.StartTime;
|
||||
lastPosition = h.X;
|
||||
}
|
||||
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case Fruit _:
|
||||
Replay.Frames.Add(new CatchReplayFrame(obj.StartTime, obj.X));
|
||||
moveToNext(obj);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -42,7 +108,7 @@ namespace osu.Game.Rulesets.Catch.Replays
|
||||
case BananaShower.Banana _:
|
||||
case TinyDroplet _:
|
||||
case Droplet _:
|
||||
Replay.Frames.Add(new CatchReplayFrame(nestedObj.StartTime, nestedObj.X));
|
||||
moveToNext(nestedObj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,26 +3,54 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
public class CatchFramedReplayInputHandler : FramedReplayInputHandler
|
||||
public class CatchFramedReplayInputHandler : FramedReplayInputHandler<CatchReplayFrame>
|
||||
{
|
||||
public CatchFramedReplayInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates() => new List<InputState>
|
||||
protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0;
|
||||
|
||||
protected float? Position
|
||||
{
|
||||
new CatchReplayState
|
||||
get
|
||||
{
|
||||
PressedActions = new List<CatchAction> { CatchAction.PositionUpdate },
|
||||
CatcherX = ((CatchReplayFrame)CurrentFrame).MouseX
|
||||
},
|
||||
new CatchReplayState { PressedActions = new List<CatchAction>() },
|
||||
};
|
||||
if (!HasFrames)
|
||||
return null;
|
||||
|
||||
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
||||
}
|
||||
}
|
||||
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
if (!Position.HasValue) return new List<InputState>();
|
||||
|
||||
var actions = new List<CatchAction>();
|
||||
|
||||
if (CurrentFrame.Dashing)
|
||||
actions.Add(CatchAction.Dash);
|
||||
|
||||
if (Position.Value > CurrentFrame.Position)
|
||||
actions.Add(CatchAction.MoveRight);
|
||||
else if (Position.Value < CurrentFrame.Position)
|
||||
actions.Add(CatchAction.MoveLeft);
|
||||
|
||||
return new List<InputState>
|
||||
{
|
||||
new CatchReplayState
|
||||
{
|
||||
PressedActions = actions,
|
||||
CatcherX = Position.Value
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public class CatchReplayState : ReplayState<CatchAction>
|
||||
{
|
||||
|
||||
@@ -1,17 +1,34 @@
|
||||
// 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;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Replays
|
||||
{
|
||||
public class CatchReplayFrame : ReplayFrame
|
||||
public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame
|
||||
{
|
||||
public override bool IsImportant => MouseX > 0;
|
||||
public float Position;
|
||||
public bool Dashing;
|
||||
|
||||
public CatchReplayFrame(double time, float? x = null)
|
||||
: base(time, x ?? -1, null, ReplayButtonState.None)
|
||||
public CatchReplayFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public CatchReplayFrame(double time, float? position = null, bool dashing = false)
|
||||
: base(time)
|
||||
{
|
||||
Position = position ?? -1;
|
||||
Dashing = dashing;
|
||||
}
|
||||
|
||||
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
|
||||
{
|
||||
Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
|
||||
Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,957 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 500.0,
|
||||
"Position": 96.0
|
||||
}, {
|
||||
"StartTime": 562.0,
|
||||
"Position": 100.84
|
||||
}, {
|
||||
"StartTime": 625.0,
|
||||
"Position": 125.0
|
||||
}, {
|
||||
"StartTime": 687.0,
|
||||
"Position": 152.84
|
||||
}, {
|
||||
"StartTime": 750.0,
|
||||
"Position": 191.0
|
||||
}, {
|
||||
"StartTime": 812.0,
|
||||
"Position": 212.84
|
||||
}, {
|
||||
"StartTime": 875.0,
|
||||
"Position": 217.0
|
||||
}, {
|
||||
"StartTime": 937.0,
|
||||
"Position": 234.84
|
||||
}, {
|
||||
"StartTime": 1000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 1062.0,
|
||||
"Position": 267.84
|
||||
}, {
|
||||
"StartTime": 1125.0,
|
||||
"Position": 284.0
|
||||
}, {
|
||||
"StartTime": 1187.0,
|
||||
"Position": 311.84
|
||||
}, {
|
||||
"StartTime": 1250.0,
|
||||
"Position": 350.0
|
||||
}, {
|
||||
"StartTime": 1312.0,
|
||||
"Position": 359.84
|
||||
}, {
|
||||
"StartTime": 1375.0,
|
||||
"Position": 367.0
|
||||
}, {
|
||||
"StartTime": 1437.0,
|
||||
"Position": 400.84
|
||||
}, {
|
||||
"StartTime": 1500.0,
|
||||
"Position": 416.0
|
||||
}, {
|
||||
"StartTime": 1562.0,
|
||||
"Position": 377.159973
|
||||
}, {
|
||||
"StartTime": 1625.0,
|
||||
"Position": 367.0
|
||||
}, {
|
||||
"StartTime": 1687.0,
|
||||
"Position": 374.159973
|
||||
}, {
|
||||
"StartTime": 1750.0,
|
||||
"Position": 353.0
|
||||
}, {
|
||||
"StartTime": 1812.0,
|
||||
"Position": 329.159973
|
||||
}, {
|
||||
"StartTime": 1875.0,
|
||||
"Position": 288.0
|
||||
}, {
|
||||
"StartTime": 1937.0,
|
||||
"Position": 259.159973
|
||||
}, {
|
||||
"StartTime": 2000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 2058.0,
|
||||
"Position": 232.44
|
||||
}, {
|
||||
"StartTime": 2116.0,
|
||||
"Position": 222.879974
|
||||
}, {
|
||||
"StartTime": 2174.0,
|
||||
"Position": 185.319992
|
||||
}, {
|
||||
"StartTime": 2232.0,
|
||||
"Position": 177.76001
|
||||
}, {
|
||||
"StartTime": 2290.0,
|
||||
"Position": 162.200012
|
||||
}, {
|
||||
"StartTime": 2348.0,
|
||||
"Position": 158.639984
|
||||
}, {
|
||||
"StartTime": 2406.0,
|
||||
"Position": 111.079994
|
||||
}, {
|
||||
"StartTime": 2500.0,
|
||||
"Position": 96.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 3000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 3000.0,
|
||||
"Position": 18.0
|
||||
}, {
|
||||
"StartTime": 3062.0,
|
||||
"Position": 482.0
|
||||
}, {
|
||||
"StartTime": 3125.0,
|
||||
"Position": 243.0
|
||||
}, {
|
||||
"StartTime": 3187.0,
|
||||
"Position": 332.0
|
||||
}, {
|
||||
"StartTime": 3250.0,
|
||||
"Position": 477.0
|
||||
}, {
|
||||
"StartTime": 3312.0,
|
||||
"Position": 376.0
|
||||
}, {
|
||||
"StartTime": 3375.0,
|
||||
"Position": 104.0
|
||||
}, {
|
||||
"StartTime": 3437.0,
|
||||
"Position": 156.0
|
||||
}, {
|
||||
"StartTime": 3500.0,
|
||||
"Position": 135.0
|
||||
}, {
|
||||
"StartTime": 3562.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 3625.0,
|
||||
"Position": 360.0
|
||||
}, {
|
||||
"StartTime": 3687.0,
|
||||
"Position": 199.0
|
||||
}, {
|
||||
"StartTime": 3750.0,
|
||||
"Position": 239.0
|
||||
}, {
|
||||
"StartTime": 3812.0,
|
||||
"Position": 326.0
|
||||
}, {
|
||||
"StartTime": 3875.0,
|
||||
"Position": 393.0
|
||||
}, {
|
||||
"StartTime": 3937.0,
|
||||
"Position": 470.0
|
||||
}, {
|
||||
"StartTime": 4000.0,
|
||||
"Position": 136.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 4500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 4500.0,
|
||||
"Position": 317.0
|
||||
}, {
|
||||
"StartTime": 4562.0,
|
||||
"Position": 354.0
|
||||
}, {
|
||||
"StartTime": 4625.0,
|
||||
"Position": 414.0
|
||||
}, {
|
||||
"StartTime": 4687.0,
|
||||
"Position": 39.0
|
||||
}, {
|
||||
"StartTime": 4750.0,
|
||||
"Position": 172.0
|
||||
}, {
|
||||
"StartTime": 4812.0,
|
||||
"Position": 479.0
|
||||
}, {
|
||||
"StartTime": 4875.0,
|
||||
"Position": 18.0
|
||||
}, {
|
||||
"StartTime": 4937.0,
|
||||
"Position": 151.0
|
||||
}, {
|
||||
"StartTime": 5000.0,
|
||||
"Position": 342.0
|
||||
}, {
|
||||
"StartTime": 5062.0,
|
||||
"Position": 400.0
|
||||
}, {
|
||||
"StartTime": 5125.0,
|
||||
"Position": 420.0
|
||||
}, {
|
||||
"StartTime": 5187.0,
|
||||
"Position": 90.0
|
||||
}, {
|
||||
"StartTime": 5250.0,
|
||||
"Position": 220.0
|
||||
}, {
|
||||
"StartTime": 5312.0,
|
||||
"Position": 80.0
|
||||
}, {
|
||||
"StartTime": 5375.0,
|
||||
"Position": 421.0
|
||||
}, {
|
||||
"StartTime": 5437.0,
|
||||
"Position": 473.0
|
||||
}, {
|
||||
"StartTime": 5500.0,
|
||||
"Position": 97.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 6000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 6000.0,
|
||||
"Position": 105.0
|
||||
}, {
|
||||
"StartTime": 6062.0,
|
||||
"Position": 249.0
|
||||
}, {
|
||||
"StartTime": 6125.0,
|
||||
"Position": 163.0
|
||||
}, {
|
||||
"StartTime": 6187.0,
|
||||
"Position": 194.0
|
||||
}, {
|
||||
"StartTime": 6250.0,
|
||||
"Position": 106.0
|
||||
}, {
|
||||
"StartTime": 6312.0,
|
||||
"Position": 212.0
|
||||
}, {
|
||||
"StartTime": 6375.0,
|
||||
"Position": 257.0
|
||||
}, {
|
||||
"StartTime": 6437.0,
|
||||
"Position": 461.0
|
||||
}, {
|
||||
"StartTime": 6500.0,
|
||||
"Position": 79.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 7000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 7000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 7062.0,
|
||||
"Position": 294.84
|
||||
}, {
|
||||
"StartTime": 7125.0,
|
||||
"Position": 279.0
|
||||
}, {
|
||||
"StartTime": 7187.0,
|
||||
"Position": 309.84
|
||||
}, {
|
||||
"StartTime": 7250.0,
|
||||
"Position": 336.0
|
||||
}, {
|
||||
"StartTime": 7312.0,
|
||||
"Position": 322.16
|
||||
}, {
|
||||
"StartTime": 7375.0,
|
||||
"Position": 308.0
|
||||
}, {
|
||||
"StartTime": 7437.0,
|
||||
"Position": 263.16
|
||||
}, {
|
||||
"StartTime": 7500.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 7562.0,
|
||||
"Position": 261.84
|
||||
}, {
|
||||
"StartTime": 7625.0,
|
||||
"Position": 277.0
|
||||
}, {
|
||||
"StartTime": 7687.0,
|
||||
"Position": 318.84
|
||||
}, {
|
||||
"StartTime": 7750.0,
|
||||
"Position": 336.0
|
||||
}, {
|
||||
"StartTime": 7803.0,
|
||||
"Position": 305.04
|
||||
}, {
|
||||
"StartTime": 7857.0,
|
||||
"Position": 307.76
|
||||
}, {
|
||||
"StartTime": 7910.0,
|
||||
"Position": 297.8
|
||||
}, {
|
||||
"StartTime": 8000.0,
|
||||
"Position": 256.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 8500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 8500.0,
|
||||
"Position": 32.0
|
||||
}, {
|
||||
"StartTime": 8562.0,
|
||||
"Position": 22.8515015
|
||||
}, {
|
||||
"StartTime": 8625.0,
|
||||
"Position": 28.5659637
|
||||
}, {
|
||||
"StartTime": 8687.0,
|
||||
"Position": 50.3433228
|
||||
}, {
|
||||
"StartTime": 8750.0,
|
||||
"Position": 56.58974
|
||||
}, {
|
||||
"StartTime": 8812.0,
|
||||
"Position": 64.23422
|
||||
}, {
|
||||
"StartTime": 8875.0,
|
||||
"Position": 67.7117844
|
||||
}, {
|
||||
"StartTime": 8937.0,
|
||||
"Position": 90.52607
|
||||
}, {
|
||||
"StartTime": 9000.0,
|
||||
"Position": 101.81015
|
||||
}, {
|
||||
"StartTime": 9062.0,
|
||||
"Position": 113.478188
|
||||
}, {
|
||||
"StartTime": 9125.0,
|
||||
"Position": 159.414444
|
||||
}, {
|
||||
"StartTime": 9187.0,
|
||||
"Position": 155.1861
|
||||
}, {
|
||||
"StartTime": 9250.0,
|
||||
"Position": 179.600418
|
||||
}, {
|
||||
"StartTime": 9312.0,
|
||||
"Position": 212.293015
|
||||
}, {
|
||||
"StartTime": 9375.0,
|
||||
"Position": 197.2076
|
||||
}, {
|
||||
"StartTime": 9437.0,
|
||||
"Position": 243.438324
|
||||
}, {
|
||||
"StartTime": 9500.0,
|
||||
"Position": 237.2304
|
||||
}, {
|
||||
"StartTime": 9562.0,
|
||||
"Position": 241.253983
|
||||
}, {
|
||||
"StartTime": 9625.0,
|
||||
"Position": 258.950623
|
||||
}, {
|
||||
"StartTime": 9687.0,
|
||||
"Position": 253.3786
|
||||
}, {
|
||||
"StartTime": 9750.0,
|
||||
"Position": 270.8865
|
||||
}, {
|
||||
"StartTime": 9812.0,
|
||||
"Position": 244.38974
|
||||
}, {
|
||||
"StartTime": 9875.0,
|
||||
"Position": 242.701874
|
||||
}, {
|
||||
"StartTime": 9937.0,
|
||||
"Position": 256.2331
|
||||
}, {
|
||||
"StartTime": 10000.0,
|
||||
"Position": 270.339874
|
||||
}, {
|
||||
"StartTime": 10062.0,
|
||||
"Position": 275.9349
|
||||
}, {
|
||||
"StartTime": 10125.0,
|
||||
"Position": 297.2969
|
||||
}, {
|
||||
"StartTime": 10187.0,
|
||||
"Position": 307.834137
|
||||
}, {
|
||||
"StartTime": 10250.0,
|
||||
"Position": 321.6449
|
||||
}, {
|
||||
"StartTime": 10312.0,
|
||||
"Position": 357.746338
|
||||
}, {
|
||||
"StartTime": 10375.0,
|
||||
"Position": 358.21875
|
||||
}, {
|
||||
"StartTime": 10437.0,
|
||||
"Position": 394.943
|
||||
}, {
|
||||
"StartTime": 10500.0,
|
||||
"Position": 401.0588
|
||||
}, {
|
||||
"StartTime": 10558.0,
|
||||
"Position": 418.21347
|
||||
}, {
|
||||
"StartTime": 10616.0,
|
||||
"Position": 424.6034
|
||||
}, {
|
||||
"StartTime": 10674.0,
|
||||
"Position": 455.835754
|
||||
}, {
|
||||
"StartTime": 10732.0,
|
||||
"Position": 477.5042
|
||||
}, {
|
||||
"StartTime": 10790.0,
|
||||
"Position": 476.290955
|
||||
}, {
|
||||
"StartTime": 10848.0,
|
||||
"Position": 470.943237
|
||||
}, {
|
||||
"StartTime": 10906.0,
|
||||
"Position": 503.3372
|
||||
}, {
|
||||
"StartTime": 10999.0,
|
||||
"Position": 508.166229
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 11500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 11500.0,
|
||||
"Position": 321.0
|
||||
}, {
|
||||
"StartTime": 11562.0,
|
||||
"Position": 17.0
|
||||
}, {
|
||||
"StartTime": 11625.0,
|
||||
"Position": 173.0
|
||||
}, {
|
||||
"StartTime": 11687.0,
|
||||
"Position": 170.0
|
||||
}, {
|
||||
"StartTime": 11750.0,
|
||||
"Position": 447.0
|
||||
}, {
|
||||
"StartTime": 11812.0,
|
||||
"Position": 218.0
|
||||
}, {
|
||||
"StartTime": 11875.0,
|
||||
"Position": 394.0
|
||||
}, {
|
||||
"StartTime": 11937.0,
|
||||
"Position": 46.0
|
||||
}, {
|
||||
"StartTime": 12000.0,
|
||||
"Position": 480.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 12500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 12500.0,
|
||||
"Position": 512.0
|
||||
}, {
|
||||
"StartTime": 12562.0,
|
||||
"Position": 491.3132
|
||||
}, {
|
||||
"StartTime": 12625.0,
|
||||
"Position": 484.3089
|
||||
}, {
|
||||
"StartTime": 12687.0,
|
||||
"Position": 454.6221
|
||||
}, {
|
||||
"StartTime": 12750.0,
|
||||
"Position": 433.617767
|
||||
}, {
|
||||
"StartTime": 12812.0,
|
||||
"Position": 399.930969
|
||||
}, {
|
||||
"StartTime": 12875.0,
|
||||
"Position": 395.926666
|
||||
}, {
|
||||
"StartTime": 12937.0,
|
||||
"Position": 361.239868
|
||||
}, {
|
||||
"StartTime": 13000.0,
|
||||
"Position": 353.235535
|
||||
}, {
|
||||
"StartTime": 13062.0,
|
||||
"Position": 314.548767
|
||||
}, {
|
||||
"StartTime": 13125.0,
|
||||
"Position": 315.544434
|
||||
}, {
|
||||
"StartTime": 13187.0,
|
||||
"Position": 288.857635
|
||||
}, {
|
||||
"StartTime": 13250.0,
|
||||
"Position": 254.853333
|
||||
}, {
|
||||
"StartTime": 13312.0,
|
||||
"Position": 239.166534
|
||||
}, {
|
||||
"StartTime": 13375.0,
|
||||
"Position": 240.1622
|
||||
}, {
|
||||
"StartTime": 13437.0,
|
||||
"Position": 212.4754
|
||||
}, {
|
||||
"StartTime": 13500.0,
|
||||
"Position": 194.471069
|
||||
}, {
|
||||
"StartTime": 13562.0,
|
||||
"Position": 161.784271
|
||||
}, {
|
||||
"StartTime": 13625.0,
|
||||
"Position": 145.779968
|
||||
}, {
|
||||
"StartTime": 13687.0,
|
||||
"Position": 129.09314
|
||||
}, {
|
||||
"StartTime": 13750.0,
|
||||
"Position": 104.088837
|
||||
}, {
|
||||
"StartTime": 13812.0,
|
||||
"Position": 95.40204
|
||||
}, {
|
||||
"StartTime": 13875.0,
|
||||
"Position": 61.3977356
|
||||
}, {
|
||||
"StartTime": 13937.0,
|
||||
"Position": 56.710907
|
||||
}, {
|
||||
"StartTime": 14000.0,
|
||||
"Position": 35.7066345
|
||||
}, {
|
||||
"StartTime": 14062.0,
|
||||
"Position": 5.019806
|
||||
}, {
|
||||
"StartTime": 14125.0,
|
||||
"Position": 0.0
|
||||
}, {
|
||||
"StartTime": 14187.0,
|
||||
"Position": 39.7696266
|
||||
}, {
|
||||
"StartTime": 14250.0,
|
||||
"Position": 23.0119171
|
||||
}, {
|
||||
"StartTime": 14312.0,
|
||||
"Position": 75.94882
|
||||
}, {
|
||||
"StartTime": 14375.0,
|
||||
"Position": 98.19112
|
||||
}, {
|
||||
"StartTime": 14437.0,
|
||||
"Position": 82.12803
|
||||
}, {
|
||||
"StartTime": 14500.0,
|
||||
"Position": 118.370323
|
||||
}, {
|
||||
"StartTime": 14562.0,
|
||||
"Position": 149.307236
|
||||
}, {
|
||||
"StartTime": 14625.0,
|
||||
"Position": 168.549515
|
||||
}, {
|
||||
"StartTime": 14687.0,
|
||||
"Position": 190.486435
|
||||
}, {
|
||||
"StartTime": 14750.0,
|
||||
"Position": 186.728714
|
||||
}, {
|
||||
"StartTime": 14812.0,
|
||||
"Position": 199.665634
|
||||
}, {
|
||||
"StartTime": 14875.0,
|
||||
"Position": 228.907928
|
||||
}, {
|
||||
"StartTime": 14937.0,
|
||||
"Position": 264.844849
|
||||
}, {
|
||||
"StartTime": 15000.0,
|
||||
"Position": 271.087128
|
||||
}, {
|
||||
"StartTime": 15062.0,
|
||||
"Position": 290.024017
|
||||
}, {
|
||||
"StartTime": 15125.0,
|
||||
"Position": 302.266327
|
||||
}, {
|
||||
"StartTime": 15187.0,
|
||||
"Position": 344.203247
|
||||
}, {
|
||||
"StartTime": 15250.0,
|
||||
"Position": 356.445526
|
||||
}, {
|
||||
"StartTime": 15312.0,
|
||||
"Position": 359.382446
|
||||
}, {
|
||||
"StartTime": 15375.0,
|
||||
"Position": 401.624725
|
||||
}, {
|
||||
"StartTime": 15437.0,
|
||||
"Position": 388.561646
|
||||
}, {
|
||||
"StartTime": 15500.0,
|
||||
"Position": 423.803925
|
||||
}, {
|
||||
"StartTime": 15562.0,
|
||||
"Position": 425.740845
|
||||
}, {
|
||||
"StartTime": 15625.0,
|
||||
"Position": 449.983124
|
||||
}, {
|
||||
"StartTime": 15687.0,
|
||||
"Position": 468.920044
|
||||
}, {
|
||||
"StartTime": 15750.0,
|
||||
"Position": 492.162323
|
||||
}, {
|
||||
"StartTime": 15812.0,
|
||||
"Position": 506.784332
|
||||
}, {
|
||||
"StartTime": 15875.0,
|
||||
"Position": 474.226227
|
||||
}, {
|
||||
"StartTime": 15937.0,
|
||||
"Position": 482.978638
|
||||
}, {
|
||||
"StartTime": 16000.0,
|
||||
"Position": 446.420532
|
||||
}, {
|
||||
"StartTime": 16058.0,
|
||||
"Position": 418.4146
|
||||
}, {
|
||||
"StartTime": 16116.0,
|
||||
"Position": 425.408844
|
||||
}, {
|
||||
"StartTime": 16174.0,
|
||||
"Position": 383.402924
|
||||
}, {
|
||||
"StartTime": 16232.0,
|
||||
"Position": 363.397156
|
||||
}, {
|
||||
"StartTime": 16290.0,
|
||||
"Position": 343.391235
|
||||
}, {
|
||||
"StartTime": 16348.0,
|
||||
"Position": 328.385468
|
||||
}, {
|
||||
"StartTime": 16406.0,
|
||||
"Position": 322.3797
|
||||
}, {
|
||||
"StartTime": 16500.0,
|
||||
"Position": 291.1977
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 17000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 17000.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 17062.0,
|
||||
"Position": 228.16
|
||||
}, {
|
||||
"StartTime": 17125.0,
|
||||
"Position": 234.0
|
||||
}, {
|
||||
"StartTime": 17187.0,
|
||||
"Position": 202.16
|
||||
}, {
|
||||
"StartTime": 17250.0,
|
||||
"Position": 176.0
|
||||
}, {
|
||||
"StartTime": 17312.0,
|
||||
"Position": 210.84
|
||||
}, {
|
||||
"StartTime": 17375.0,
|
||||
"Position": 221.0
|
||||
}, {
|
||||
"StartTime": 17437.0,
|
||||
"Position": 219.84
|
||||
}, {
|
||||
"StartTime": 17500.0,
|
||||
"Position": 256.0
|
||||
}, {
|
||||
"StartTime": 17562.0,
|
||||
"Position": 219.16
|
||||
}, {
|
||||
"StartTime": 17625.0,
|
||||
"Position": 228.0
|
||||
}, {
|
||||
"StartTime": 17687.0,
|
||||
"Position": 203.16
|
||||
}, {
|
||||
"StartTime": 17750.0,
|
||||
"Position": 176.0
|
||||
}, {
|
||||
"StartTime": 17803.0,
|
||||
"Position": 174.959991
|
||||
}, {
|
||||
"StartTime": 17857.0,
|
||||
"Position": 214.23999
|
||||
}, {
|
||||
"StartTime": 17910.0,
|
||||
"Position": 228.200012
|
||||
}, {
|
||||
"StartTime": 18000.0,
|
||||
"Position": 256.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 18500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 18500.0,
|
||||
"Position": 362.0
|
||||
}, {
|
||||
"StartTime": 18559.0,
|
||||
"Position": 249.0
|
||||
}, {
|
||||
"StartTime": 18618.0,
|
||||
"Position": 357.0
|
||||
}, {
|
||||
"StartTime": 18678.0,
|
||||
"Position": 167.0
|
||||
}, {
|
||||
"StartTime": 18737.0,
|
||||
"Position": 477.0
|
||||
}, {
|
||||
"StartTime": 18796.0,
|
||||
"Position": 411.0
|
||||
}, {
|
||||
"StartTime": 18856.0,
|
||||
"Position": 254.0
|
||||
}, {
|
||||
"StartTime": 18915.0,
|
||||
"Position": 308.0
|
||||
}, {
|
||||
"StartTime": 18975.0,
|
||||
"Position": 399.0
|
||||
}, {
|
||||
"StartTime": 19034.0,
|
||||
"Position": 176.0
|
||||
}, {
|
||||
"StartTime": 19093.0,
|
||||
"Position": 14.0
|
||||
}, {
|
||||
"StartTime": 19153.0,
|
||||
"Position": 258.0
|
||||
}, {
|
||||
"StartTime": 19212.0,
|
||||
"Position": 221.0
|
||||
}, {
|
||||
"StartTime": 19271.0,
|
||||
"Position": 481.0
|
||||
}, {
|
||||
"StartTime": 19331.0,
|
||||
"Position": 92.0
|
||||
}, {
|
||||
"StartTime": 19390.0,
|
||||
"Position": 211.0
|
||||
}, {
|
||||
"StartTime": 19450.0,
|
||||
"Position": 135.0
|
||||
}]
|
||||
}, {
|
||||
"StartTime": 19875.0,
|
||||
"Objects": [{
|
||||
"StartTime": 19875.0,
|
||||
"Position": 216.0
|
||||
}, {
|
||||
"StartTime": 19937.0,
|
||||
"Position": 215.307053
|
||||
}, {
|
||||
"StartTime": 20000.0,
|
||||
"Position": 236.036865
|
||||
}, {
|
||||
"StartTime": 20062.0,
|
||||
"Position": 236.312088
|
||||
}, {
|
||||
"StartTime": 20125.0,
|
||||
"Position": 235.838928
|
||||
}, {
|
||||
"StartTime": 20187.0,
|
||||
"Position": 269.9743
|
||||
}, {
|
||||
"StartTime": 20250.0,
|
||||
"Position": 285.999146
|
||||
}, {
|
||||
"StartTime": 20312.0,
|
||||
"Position": 283.669067
|
||||
}, {
|
||||
"StartTime": 20375.0,
|
||||
"Position": 317.446747
|
||||
}, {
|
||||
"StartTime": 20437.0,
|
||||
"Position": 330.750275
|
||||
}, {
|
||||
"StartTime": 20500.0,
|
||||
"Position": 344.0156
|
||||
}, {
|
||||
"StartTime": 20562.0,
|
||||
"Position": 318.472168
|
||||
}, {
|
||||
"StartTime": 20625.0,
|
||||
"Position": 309.165466
|
||||
}, {
|
||||
"StartTime": 20687.0,
|
||||
"Position": 317.044617
|
||||
}, {
|
||||
"StartTime": 20750.0,
|
||||
"Position": 280.457367
|
||||
}, {
|
||||
"StartTime": 20812.0,
|
||||
"Position": 272.220581
|
||||
}, {
|
||||
"StartTime": 20875.0,
|
||||
"Position": 270.3294
|
||||
}, {
|
||||
"StartTime": 20937.0,
|
||||
"Position": 262.57605
|
||||
}, {
|
||||
"StartTime": 21000.0,
|
||||
"Position": 244.803329
|
||||
}, {
|
||||
"StartTime": 21062.0,
|
||||
"Position": 215.958359
|
||||
}, {
|
||||
"StartTime": 21125.0,
|
||||
"Position": 177.79332
|
||||
}, {
|
||||
"StartTime": 21187.0,
|
||||
"Position": 190.948349
|
||||
}, {
|
||||
"StartTime": 21250.0,
|
||||
"Position": 158.78334
|
||||
}, {
|
||||
"StartTime": 21312.0,
|
||||
"Position": 136.93837
|
||||
}, {
|
||||
"StartTime": 21375.0,
|
||||
"Position": 119.121056
|
||||
}, {
|
||||
"StartTime": 21437.0,
|
||||
"Position": 132.387573
|
||||
}, {
|
||||
"StartTime": 21500.0,
|
||||
"Position": 124.503014
|
||||
}, {
|
||||
"StartTime": 21562.0,
|
||||
"Position": 118.749374
|
||||
}, {
|
||||
"StartTime": 21625.0,
|
||||
"Position": 123.165535
|
||||
}, {
|
||||
"StartTime": 21687.0,
|
||||
"Position": 96.02999
|
||||
}, {
|
||||
"StartTime": 21750.0,
|
||||
"Position": 118.547928
|
||||
}, {
|
||||
"StartTime": 21812.0,
|
||||
"Position": 128.856232
|
||||
}, {
|
||||
"StartTime": 21875.0,
|
||||
"Position": 124.28746
|
||||
}, {
|
||||
"StartTime": 21937.0,
|
||||
"Position": 150.754929
|
||||
}, {
|
||||
"StartTime": 22000.0,
|
||||
"Position": 149.528732
|
||||
}, {
|
||||
"StartTime": 22062.0,
|
||||
"Position": 145.1691
|
||||
}, {
|
||||
"StartTime": 22125.0,
|
||||
"Position": 182.802155
|
||||
}, {
|
||||
"StartTime": 22187.0,
|
||||
"Position": 178.6452
|
||||
}, {
|
||||
"StartTime": 22250.0,
|
||||
"Position": 213.892181
|
||||
}, {
|
||||
"StartTime": 22312.0,
|
||||
"Position": 218.713028
|
||||
}, {
|
||||
"StartTime": 22375.0,
|
||||
"Position": 240.4715
|
||||
}, {
|
||||
"StartTime": 22437.0,
|
||||
"Position": 239.371887
|
||||
}, {
|
||||
"StartTime": 22500.0,
|
||||
"Position": 261.907257
|
||||
}, {
|
||||
"StartTime": 22562.0,
|
||||
"Position": 314.353119
|
||||
}, {
|
||||
"StartTime": 22625.0,
|
||||
"Position": 299.273376
|
||||
}, {
|
||||
"StartTime": 22687.0,
|
||||
"Position": 356.98288
|
||||
}, {
|
||||
"StartTime": 22750.0,
|
||||
"Position": 339.078552
|
||||
}, {
|
||||
"StartTime": 22812.0,
|
||||
"Position": 377.8958
|
||||
}, {
|
||||
"StartTime": 22875.0,
|
||||
"Position": 398.054047
|
||||
}, {
|
||||
"StartTime": 22937.0,
|
||||
"Position": 398.739441
|
||||
}, {
|
||||
"StartTime": 23000.0,
|
||||
"Position": 407.178467
|
||||
}, {
|
||||
"StartTime": 23062.0,
|
||||
"Position": 444.8687
|
||||
}, {
|
||||
"StartTime": 23125.0,
|
||||
"Position": 417.069977
|
||||
}, {
|
||||
"StartTime": 23187.0,
|
||||
"Position": 454.688477
|
||||
}, {
|
||||
"StartTime": 23250.0,
|
||||
"Position": 428.9612
|
||||
}, {
|
||||
"StartTime": 23312.0,
|
||||
"Position": 441.92807
|
||||
}, {
|
||||
"StartTime": 23375.0,
|
||||
"Position": 439.749878
|
||||
}, {
|
||||
"StartTime": 23433.0,
|
||||
"Position": 455.644684
|
||||
}, {
|
||||
"StartTime": 23491.0,
|
||||
"Position": 440.7359
|
||||
}, {
|
||||
"StartTime": 23549.0,
|
||||
"Position": 430.0944
|
||||
}, {
|
||||
"StartTime": 23607.0,
|
||||
"Position": 420.796173
|
||||
}, {
|
||||
"StartTime": 23665.0,
|
||||
"Position": 435.897461
|
||||
}, {
|
||||
"StartTime": 23723.0,
|
||||
"Position": 418.462555
|
||||
}, {
|
||||
"StartTime": 23781.0,
|
||||
"Position": 405.53775
|
||||
}, {
|
||||
"StartTime": 23874.0,
|
||||
"Position": 408.720825
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
osu file format v14
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8.3
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
500,500,4,2,1,50,1,0
|
||||
13426,-100,4,3,1,45,0,0
|
||||
14884,-100,4,2,1,50,0,0
|
||||
|
||||
[HitObjects]
|
||||
96,192,500,6,0,L|416:192,2,320
|
||||
256,192,3000,12,0,4000,0:0:0:0:
|
||||
256,192,4500,12,0,5500,0:0:0:0:
|
||||
256,192,6000,12,0,6500,0:0:0:0:
|
||||
256,128,7000,6,0,L|352:128,4,80
|
||||
32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
|
||||
256,192,11500,12,0,12000,0:0:0:0:
|
||||
512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280
|
||||
256,256,17000,6,0,L|160:256,4,80
|
||||
256,192,18500,12,0,19450,0:0:0:0:
|
||||
216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280
|
||||
@@ -24,17 +24,13 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
switch (obj)
|
||||
{
|
||||
case JuiceStream stream:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
foreach (var _ in stream.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case BananaShower shower:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Fruit _:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
{
|
||||
foreach (var nested in stream.NestedHitObjects)
|
||||
{
|
||||
yield return new ConvertValue
|
||||
{
|
||||
StartTime = nested.StartTime,
|
||||
Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new ConvertValue
|
||||
{
|
||||
StartTime = hitObject.StartTime,
|
||||
Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
||||
}
|
||||
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// A sane value to account for osu!stable using ints everwhere.
|
||||
/// </summary>
|
||||
private const float conversion_lenience = 2;
|
||||
|
||||
public double StartTime;
|
||||
public float Position;
|
||||
|
||||
public bool Equals(ConvertValue other)
|
||||
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ using osu.Game.Rulesets.Catch.UI;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
@@ -29,16 +28,14 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
}
|
||||
|
||||
protected override Beatmap CreateBeatmap()
|
||||
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 6,
|
||||
}
|
||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
||||
Ruleset = ruleset.RulesetInfo
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ using NUnit.Framework;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchPlayer() : base(new CatchRuleset())
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchStacker()
|
||||
@@ -16,19 +15,18 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
}
|
||||
|
||||
protected override Beatmap CreateBeatmap()
|
||||
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 6,
|
||||
}
|
||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
||||
Ruleset = ruleset.RulesetInfo
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for (int i = 0; i < 512; i++)
|
||||
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ using osu.Game.Tests.Visual;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatcherArea : OsuTestCase
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
|
||||
@@ -6,17 +6,15 @@ using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCaseFruitObjects : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
@@ -62,8 +60,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
Scale = 1.5f,
|
||||
};
|
||||
|
||||
fruit.ComboColour = colourForRrepesentation(fruit.VisualRepresentation);
|
||||
|
||||
return new DrawableFruit(fruit)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
@@ -74,31 +70,5 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
LifetimeEnd = double.PositiveInfinity,
|
||||
};
|
||||
}
|
||||
|
||||
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
||||
{
|
||||
switch (representation)
|
||||
{
|
||||
default:
|
||||
case FruitVisualRepresentation.Pear:
|
||||
return new Color4(17, 136, 170, 255);
|
||||
case FruitVisualRepresentation.Grape:
|
||||
return new Color4(204, 102, 0, 255);
|
||||
case FruitVisualRepresentation.Raspberry:
|
||||
return new Color4(121, 9, 13, 255);
|
||||
case FruitVisualRepresentation.Pineapple:
|
||||
return new Color4(102, 136, 0, 255);
|
||||
case FruitVisualRepresentation.Banana:
|
||||
switch (RNG.Next(0, 3))
|
||||
{
|
||||
default:
|
||||
return new Color4(255, 240, 0, 255);
|
||||
case 1:
|
||||
return new Color4(255, 192, 0, 255);
|
||||
case 2:
|
||||
return new Color4(214, 221, 28, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Game.Rulesets.Catch.Objects;
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseHyperdash()
|
||||
@@ -16,9 +15,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
}
|
||||
|
||||
protected override Beatmap CreateBeatmap()
|
||||
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
|
||||
|
||||
|
||||
for (int i = 0; i < 512; i++)
|
||||
if (i % 5 < 3)
|
||||
|
||||
@@ -5,7 +5,7 @@ using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
[TestFixture]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
|
||||
@@ -35,10 +35,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
ScaledContent.AddRange(new Drawable[]
|
||||
{
|
||||
content = new Container<Drawable>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
explodingFruitContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@@ -49,7 +45,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
ExplodingFruitTarget = explodingFruitContainer,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
}
|
||||
},
|
||||
content = new Container<Drawable>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
@@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
|
||||
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherArea : Container, IKeyBindingHandler<CatchAction>
|
||||
public class CatcherArea : Container
|
||||
{
|
||||
public const float CATCHER_SIZE = 172;
|
||||
|
||||
@@ -54,7 +54,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (caughtFruit == null) return;
|
||||
|
||||
caughtFruit.AccentColour = fruit.AccentColour;
|
||||
caughtFruit.RelativePositionAxes = Axes.None;
|
||||
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
||||
|
||||
@@ -84,16 +83,14 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
if (action != CatchAction.PositionUpdate) return false;
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
CatchFramedReplayInputHandler.CatchReplayState state = (CatchFramedReplayInputHandler.CatchReplayState)GetContainingInputManager().CurrentState;
|
||||
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||
|
||||
if (state.CatcherX.HasValue)
|
||||
if (state?.CatcherX != null)
|
||||
MovableCatcher.X = state.CatcherX.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnReleased(CatchAction action) => false;
|
||||
|
||||
@@ -32,14 +32,30 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.11\lib\net45\OpenTK.dll</HintPath>
|
||||
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Collections" />
|
||||
<Reference Include="System.Core" />
|
||||
@@ -79,6 +95,7 @@
|
||||
<Compile Include="Objects\Fruit.cs" />
|
||||
<Compile Include="Objects\TinyDroplet.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tests\CatchBeatmapConversionTest.cs" />
|
||||
<Compile Include="Tests\TestCaseBananaShower.cs" />
|
||||
<Compile Include="Tests\TestCaseCatcherArea.cs" />
|
||||
<Compile Include="Tests\TestCaseCatchStacker.cs" />
|
||||
@@ -112,12 +129,20 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic-expected-conversion.json" />
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic.osu" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.8.1" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.11" targetFramework="net461" />
|
||||
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
|
||||
</packages>
|
||||
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <summary>
|
||||
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
|
||||
/// </summary>
|
||||
public readonly List<StageDefinition> Stages = new List<StageDefinition>();
|
||||
public List<StageDefinition> Stages = new List<StageDefinition>();
|
||||
|
||||
/// <summary>
|
||||
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
|
||||
@@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ManiaBeatmap"/>.
|
||||
/// </summary>
|
||||
/// <param name="initialStage">The initial stage.</param>
|
||||
public ManiaBeatmap(StageDefinition initialStage)
|
||||
/// <param name="defaultStage">The initial stages.</param>
|
||||
public ManiaBeatmap(StageDefinition defaultStage)
|
||||
{
|
||||
Stages.Add(initialStage);
|
||||
Stages.Add(defaultStage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
yield break;
|
||||
}
|
||||
|
||||
var objects = IsForCurrentRuleset ? generateSpecific(original) : generateConverted(original);
|
||||
var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
|
||||
|
||||
if (objects == null)
|
||||
yield break;
|
||||
@@ -110,10 +110,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// Method that generates hit objects for osu!mania specific beatmaps.
|
||||
/// </summary>
|
||||
/// <param name="original">The original hit object.</param>
|
||||
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
|
||||
/// <returns>The hit objects generated.</returns>
|
||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
|
||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap)
|
||||
{
|
||||
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
|
||||
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
||||
|
||||
Pattern newPattern = generator.Generate();
|
||||
lastPattern = newPattern;
|
||||
@@ -125,26 +126,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// Method that generates hit objects for non-osu!mania beatmaps.
|
||||
/// </summary>
|
||||
/// <param name="original">The original hit object.</param>
|
||||
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
|
||||
/// <returns>The hit objects generated.</returns>
|
||||
private IEnumerable<ManiaHitObject> generateConverted(HitObject original)
|
||||
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap)
|
||||
{
|
||||
var endTimeData = original as IHasEndTime;
|
||||
var distanceData = original as IHasDistance;
|
||||
var positionData = original as IHasPosition;
|
||||
|
||||
// Following lines currently commented out to appease resharper
|
||||
|
||||
Patterns.PatternGenerator conversion = null;
|
||||
|
||||
if (distanceData != null)
|
||||
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
|
||||
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
||||
else if (endTimeData != null)
|
||||
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
|
||||
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
|
||||
else if (positionData != null)
|
||||
{
|
||||
computeDensity(original.StartTime);
|
||||
|
||||
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
|
||||
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
||||
|
||||
recordNote(original.StartTime, positionData.Position);
|
||||
}
|
||||
@@ -153,10 +153,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
return null;
|
||||
|
||||
Pattern newPattern = conversion.Generate();
|
||||
lastPattern = newPattern;
|
||||
|
||||
var stairPatternGenerator = conversion as HitObjectPatternGenerator;
|
||||
lastStair = stairPatternGenerator?.StairType ?? lastStair;
|
||||
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
||||
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
||||
|
||||
return newPattern.HitObjects;
|
||||
}
|
||||
@@ -166,8 +165,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// </summary>
|
||||
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
|
||||
{
|
||||
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
|
||||
: base(random, hitObject, beatmap, previousPattern)
|
||||
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
|
||||
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -216,7 +215,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount;
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user