1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-13 21:13:39 +08:00

Compare commits

...

32 Commits

256 changed files with 20099 additions and 2 deletions
+80
View File
@@ -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;
}
}
}
+63
View File
@@ -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)
{
}
}
}
+23
View File
@@ -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;
}
}
+119
View File
@@ -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
}
}
+23
View File
@@ -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;
}
}
}
+35
View File
@@ -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")]
+95
View File
@@ -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();
}
}
}
+15
View File
@@ -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
}
}
+114
View File
@@ -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
}
}
};
}
}
}
+147
View File
@@ -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
};
}
}
}
+59
View File
@@ -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
};
}
}
}
+16
View File
@@ -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.
@@ -0,0 +1,5 @@
info face="vitaru!font" size=100 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=4 padding=0,0,0,0 spacing=4,4 outline=0
common lineHeight=100 base=83 scaleW=1024 scaleH=1024 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
page id=0 file="symcolFont_0.png"
chars count=1
char id=57419 x=495 y=537 width=88 height=89 xoffset=0 yoffset=8 xadvance=89 page=0 chnl=15
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

@@ -0,0 +1,76 @@
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Shape.Objects;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Types;
using System;
using osu.Game.Audio;
using System.Linq;
namespace osu.Game.Rulesets.Shape.Beatmaps
{
internal class ShapeBeatmapConverter : BeatmapConverter<ShapeHitObject>
{
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasPosition) };
protected override IEnumerable<ShapeHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{
var curveData = original as IHasCurve;
var endTimeData = original as IHasEndTime;
var positionData = original as IHasPosition;
var comboData = original as IHasCombo;
List<SampleInfo> samples = original.Samples;
bool isSquare = samples.Any(s => s.Name == SampleInfo.HIT_WHISTLE);
bool isTriangle = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
bool isX = samples.Any(s => s.Name == SampleInfo.HIT_CLAP);
if (isSquare)
{
yield return new BaseShape
{
StartTime = original.StartTime,
StartPosition = positionData?.Position ?? Vector2.Zero,
Samples = original.Samples,
NewCombo = comboData?.NewCombo ?? false,
ShapeID = 2,
};
}
else if (isTriangle)
{
yield return new BaseShape
{
StartTime = original.StartTime,
StartPosition = positionData?.Position ?? Vector2.Zero,
Samples = original.Samples,
NewCombo = comboData?.NewCombo ?? false,
ShapeID = 3,
};
}
else if (isX)
{
yield return new BaseShape
{
StartTime = original.StartTime,
StartPosition = positionData?.Position ?? Vector2.Zero,
Samples = original.Samples,
NewCombo = comboData?.NewCombo ?? false,
ShapeID = 4,
};
}
else
{
yield return new BaseShape
{
StartTime = original.StartTime,
StartPosition = positionData?.Position ?? Vector2.Zero,
Samples = original.Samples,
NewCombo = comboData?.NewCombo ?? false,
ShapeID = 1,
};
}
}
}
}
@@ -0,0 +1,13 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Shape.Objects;
namespace osu.Game.Rulesets.Shape.Beatmaps
{
internal class ShapeBeatmapProcessor : BeatmapProcessor<ShapeHitObject>
{
public override void PostProcess(Beatmap<ShapeHitObject> beatmap)
{
base.PostProcess(beatmap);
}
}
}
@@ -0,0 +1,18 @@
using System;
using OpenTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Shape.Objects.Drawables;
using osu.Framework.Extensions;
namespace osu.Game.Rulesets.Shape.Judgements
{
public class ShapeJudgement : Judgement
{
/// <summary>
/// The positional hit offset.
/// </summary>
public Vector2 PositionOffset;
public ComboResult Combo;
}
}
+74
View File
@@ -0,0 +1,74 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Shape.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Shape.Mods
{
public class ShapeModNoFail : ModNoFail
{
}
public class ShapeModEasy : ModEasy
{
}
public class ShapeModHidden : ModHidden
{
public override string Description => @"Notes fade out";
public override double ScoreMultiplier => 1.06;
}
public class ShapeModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.08;
public override bool Ranked => true;
}
public class ShapeModSuddenDeath : ModSuddenDeath
{
public override string Description => "Don't Miss";
public override bool Ranked => true;
}
public class ShapeModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.3;
}
public class ShapeModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.16;
}
public class ShapeModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.3;
}
public class ShapeModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.16;
}
public class ShapeModFlashlight : ModFlashlight
{
public override string Description => @"I don't even know how to play with this";
public override double ScoreMultiplier => 1.18;
}
public class ShapeRelax : ModRelax
{
public override bool Ranked => false;
}
public class ShapeModAutoplay : ModAutoplay<ShapeHitObject>
{
protected override Score CreateReplayScore(Beatmap<ShapeHitObject> beatmap) => new Score
{
};
}
}
@@ -0,0 +1,17 @@
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets.Shape;
using Symcol.Rulesets.Core.Multiplayer.Screens;
namespace osu.Game.Rulesets.Shape.Multi
{
public class ShapeLobbyItem : RulesetLobbyItem
{
public override Texture Icon => ShapeRuleset.ShapeTextures.Get("icon");
public override string RulesetName => "Shape!";
//public override Texture Background => ShapeRuleset.ShapeTextures.Get("VitaruTouhosuModeTrue2560x1440");
public override RulesetLobbyScreen RulesetLobbyScreen => new ShapeLobbyScreen();
}
}
@@ -0,0 +1,14 @@
using Symcol.Rulesets.Core.Multiplayer.Networking;
using Symcol.Rulesets.Core.Multiplayer.Screens;
using System;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Shape.Multi
{
public class ShapeLobbyScreen : RulesetLobbyScreen
{
public override string RulesetName => "shape";
public override RulesetMatchScreen MatchScreen => new ShapeMatchScreen(RulesetNetworkingClientHandler);
}
}
@@ -0,0 +1,15 @@
using Symcol.Rulesets.Core.Multiplayer.Networking;
using Symcol.Rulesets.Core.Multiplayer.Screens;
namespace osu.Game.Rulesets.Shape.Multi
{
public class ShapeMatchScreen : RulesetMatchScreen
{
public readonly RulesetNetworkingClientHandler ShapeNetworkingClientHandler;
public ShapeMatchScreen(RulesetNetworkingClientHandler shapeNetworkingClientHandler) : base(shapeNetworkingClientHandler)
{
ShapeNetworkingClientHandler = shapeNetworkingClientHandler;
}
}
}
@@ -0,0 +1,43 @@
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace osu.Game.Rulesets.Shape.Objects
{
public class BaseShape : ShapeHitObject
{
public override HitObjectType Type => HitObjectType.Shape;
public Vector2 StartPosition { get; set; }
public float ShapeSize { get; set; } = 40;
public int ShapeID { get; set; }
/// <summary>
/// The hit window that results in a "GREAT" hit.
/// </summary>
public double HitWindowGreat = 35;
/// <summary>
/// The hit window that results in a "GOOD" hit.
/// </summary>
public double HitWindowGood = 80;
/// <summary>
/// The hit window that results in a "MISS".
/// </summary>
public double HitWindowMiss = 95;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
}
}
}
@@ -0,0 +1,217 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Shape.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Shape.Judgements;
using System.Linq;
using System;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Shape.Objects.Drawables
{
public class DrawableBaseShape : DrawableShapeHitObject<ShapeHitObject>
{
private BaseDial baseDial;
private ShapeCircle circle;
private ShapeSquare square;
private ShapeTriangle triangle;
private ShapeX x;
private bool validKeyPressed;
private readonly BaseShape shape;
public DrawableBaseShape(BaseShape Shape) : base(Shape)
{
shape = Shape;
Position = shape.StartPosition;
Alpha = 0;
AlwaysPresent = true;
Anchor = Anchor.TopLeft;
Origin = Anchor.Centre;
}
protected override void LoadComplete()
{
base.LoadComplete();
LifetimeStart = shape.StartTime - (TIME_PREEMPT + 1000f);
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
base.CheckForJudgements(userTriggered, timeOffset);
if (LifetimeStart <= Time.Current)
{
if (!userTriggered)
{
if (timeOffset > shape.HitWindowGood)
{
AddJudgement(new ShapeJudgement { Result = HitResult.Miss });
Delete();
}
return;
}
double hitOffset = Math.Abs(timeOffset);
if (hitOffset > shape.HitWindowMiss)
return;
if (!validKeyPressed)
{
AddJudgement(new ShapeJudgement { Result = HitResult.Miss });
Delete();
}
else if (hitOffset < shape.HitWindowGood)
{
AddJudgement(new ShapeJudgement { Result = hitOffset < shape.HitWindowGreat ? HitResult.Great : HitResult.Good });
Delete();
}
else
{
AddJudgement(new ShapeJudgement { Result = HitResult.Miss });
Delete();
}
}
}
public override bool OnPressed(ShapeAction action)
{
if (LifetimeStart <= Time.Current)
{
switch (shape.ShapeID)
{
case 1:
ShapeAction[] hitActionsNorth = { ShapeAction.SouthLeftButton, ShapeAction.SouthRightButton };
validKeyPressed = hitActionsNorth.Contains(action);
return UpdateJudgement(true);
case 2:
ShapeAction[] hitActionsSouth = { ShapeAction.WestLeftButton, ShapeAction.WestRightButton };
validKeyPressed = hitActionsSouth.Contains(action);
return UpdateJudgement(true);
case 3:
ShapeAction[] hitActionsWest = { ShapeAction.EastLeftButton, ShapeAction.EastRightButton };
validKeyPressed = hitActionsWest.Contains(action);
return UpdateJudgement(true);
case 4:
ShapeAction[] hitActionsEast = { ShapeAction.NorthLeftButton, ShapeAction.NorthRightButton };
validKeyPressed = hitActionsEast.Contains(action);
return UpdateJudgement(true);
}
}
return UpdateJudgement(false);
}
protected override void Update()
{
base.Update();
if (LifetimeStart <= Time.Current)
{
if (Time.Current >= (shape.StartTime - TIME_PREEMPT) - 500 && !loaded)
{
preLoad();
}
if (Time.Current >= shape.StartTime - TIME_PREEMPT && !started)
{
start();
}
}
}
private bool loaded = false;
private bool started = false;
private void preLoad()
{
loaded = true;
switch (shape.ShapeID)
{
case 1:
Children = new Drawable[]
{
baseDial = new BaseDial(shape)
{
Depth = -1,
ShapeID = shape.ShapeID,
},
circle = new ShapeCircle(shape) { Depth = -2, Colour = Color4.Red, },
};
break;
case 2:
Children = new Drawable[]
{
baseDial = new BaseDial(shape)
{
Depth = -1,
ShapeID = shape.ShapeID,
},
square = new ShapeSquare(shape) { Depth = -2, Colour = Color4.Violet, },
};
break;
case 3:
Children = new Drawable[]
{
baseDial = new BaseDial(shape)
{
Depth = -1,
ShapeID = shape.ShapeID,
},
triangle = new ShapeTriangle(shape) { Depth = -2, Colour = Color4.Green, },
};
break;
case 4:
Children = new Drawable[]
{
baseDial = new BaseDial(shape)
{
Depth = -1,
ShapeID = shape.ShapeID,
},
x = new ShapeX(shape) { Depth = -2, Colour = Color4.Blue, },
};
break;
}
}
private void start()
{
started = true;
this.FadeIn(TIME_FADEIN);
baseDial.StartSpinning(TIME_PREEMPT);
switch (shape.ShapeID)
{
case 1:
circle.Position = new Vector2(RNG.Next(-200, 200), -400);
circle.MoveTo(baseDial.Position, TIME_PREEMPT);
break;
case 2:
square.Position = new Vector2(RNG.Next(-200, 200), -400);
square.MoveTo(baseDial.Position, TIME_PREEMPT);
break;
case 3:
triangle.Position = new Vector2(RNG.Next(-200, 200), -400);
triangle.MoveTo(baseDial.Position, TIME_PREEMPT);
break;
case 4:
x.Position = new Vector2(RNG.Next(-200, 200), -400);
x.MoveTo(baseDial.Position, TIME_PREEMPT);
break;
}
}
//Marks this for death
internal void Delete()
{
AlwaysPresent = true;
Alpha = 0;
LifetimeEnd = Time.Current;
Expire();
}
}
}
@@ -0,0 +1,51 @@
using osu.Game.Rulesets.Objects.Drawables;
using System;
using System.ComponentModel;
using osu.Game.Rulesets.Shape.Judgements;
using osu.Framework.Input.Bindings;
using Symcol.Rulesets.Core.HitObjects;
namespace osu.Game.Rulesets.Shape.Objects.Drawables
{
public abstract class DrawableShapeHitObject<ShapeObject> : DrawableSymcolHitObject<ShapeHitObject>, IKeyBindingHandler<ShapeAction>
where ShapeObject : ShapeHitObject
{
public float TIME_PREEMPT = 800;
public float TIME_FADEIN = 400;
public float TIME_FADEOUT = 400;
public DrawableShapeHitObject(ShapeHitObject hitObject)
: base(hitObject)
{
}
protected override void UpdateState(ArmedState state)
{
}
protected virtual void UpdatePreemptState()
{
}
protected virtual void UpdateInitialState()
{
}
public abstract bool OnPressed(ShapeAction action);
public virtual bool OnReleased(ShapeAction action) => false;
}
public enum ComboResult
{
[Description(@"")]
None,
[Description(@"Good")]
Good,
[Description(@"Amazing!")]
Perfect
}
}
@@ -0,0 +1,25 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Shape.Judgements;
using OpenTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Shape.Objects.Drawables
{
public class DrawableShapeJudgement : DrawableJudgement
{
public DrawableShapeJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
}
protected override void LoadComplete()
{
if (Judgement.Result != HitResult.Miss)
JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();
}
}
}
@@ -0,0 +1,102 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Shape.Objects.Drawables.Pieces
{
//The cat in the hat is the creepiest fucker you will ever meet
public class Arrow : Container
{
private readonly BaseShape shape;
private Container line1;
private Container line1Glow;
private Container line2;
public Arrow(BaseShape Shape)
{
shape = Shape;
}
protected override void LoadComplete()
{
base.LoadComplete();
Children = new Drawable[]
{
line1 = new Container
{
Rotation = 360 - 45,
Size = new Vector2(shape.ShapeSize / 6 , shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Depth = -2,
Colour = Color4.White,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 1,
RelativeSizeAxes = Axes.Both
},
},
},
line2 = new Container
{
Rotation = 45,
Size = new Vector2(shape.ShapeSize / 6 , shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Depth = -1,
Colour = Color4.White,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 1,
RelativeSizeAxes = Axes.Both
},
},
EdgeEffect = new EdgeEffectParameters
{
Hollow = true,
Type = EdgeEffectType.Shadow,
Colour = Color4.White.Opacity(0.25f),
Radius = shape.ShapeSize / 4,
}
},
line1Glow = new Container
{
Rotation = 360 - 45,
Size = new Vector2(shape.ShapeSize / 6 , shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Depth = 0,
Colour = Color4.White,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 0,
RelativeSizeAxes = Axes.Both
},
},
EdgeEffect = new EdgeEffectParameters
{
Hollow = true,
Type = EdgeEffectType.Shadow,
Colour = Color4.White.Opacity(0.25f),
Radius = shape.ShapeSize / 4,
}
},
};
}
}
}
@@ -0,0 +1,130 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using osu.Framework.Audio.Track;
using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Shape.Objects.Drawables.Pieces
{
public class BaseDial : BeatSyncedContainer
{
private BaseShape shape;
private ShapeCircle baseCircle;
private ShapeSquare baseSquare;
private ShapeTriangle baseTriangle;
private ShapeX baseX;
private Container arrow;
private Container box;
public int ShapeID { get; set; }
public BaseDial(BaseShape Shape)
{
shape = Shape;
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
}
protected override void LoadComplete()
{
base.LoadComplete();
Children = new Drawable[]
{
arrow = new Container
{
Depth = 0,
Origin = Anchor.BottomCentre,
Anchor = Anchor.Centre,
Size = new Vector2(shape.ShapeSize / 6 , shape.ShapeSize * 0.6f),
Colour = Color4.DarkGray,
Children = new Drawable[]
{
new Box
{
Alpha = 1,
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
new Triangle
{
Colour = Color4.White,
Size = new Vector2(shape.ShapeSize / 4),
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
},
}
},
box = new Container
{
Depth = -1,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(shape.ShapeSize / 4),
Colour = Color4.DarkGray,
Children = new Drawable[]
{
new Box
{
Alpha = 1,
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
}
},
};
switch (ShapeID)
{
case 1:
AddRange(new Drawable[]
{
baseCircle = new ShapeCircle(shape) { Colour = Color4.Gray, Depth = 1 },
});
break;
case 2:
AddRange(new Drawable[]
{
baseSquare = new ShapeSquare(shape) { Colour = Color4.Gray, Depth = 1 },
});
break;
case 3:
AddRange(new Drawable[]
{
baseTriangle = new ShapeTriangle(shape) { Colour = Color4.Gray, Depth = 1 },
});
break;
case 4:
AddRange(new Drawable[]
{
baseX = new ShapeX(shape) { Colour = Color4.Gray, Depth = 1 },
});
break;
}
}
[BackgroundDependencyLoader]
private void load()
{
}
public void StartSpinning(float time)
{
box.RotateTo(360, time);
arrow.RotateTo(360, time);
}
}
}
@@ -0,0 +1,55 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Shape.Objects.Drawables.Pieces
{
public class ShapeCircle : Container
{
private BaseShape shape;
private Container ring;
public ShapeCircle(BaseShape Shape)
{
shape = Shape;
}
protected override void LoadComplete()
{
base.LoadComplete();
Children = new Drawable[]
{
ring = new Container
{
Size = new Vector2(shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
BorderThickness = shape.ShapeSize / 6,
Depth = -2,
BorderColour = Color4.White,
CornerRadius = shape.ShapeSize / 2,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 0,
RelativeSizeAxes = Axes.Both
},
},
EdgeEffect = new EdgeEffectParameters
{
Hollow = true,
Type = EdgeEffectType.Shadow,
Colour = Color4.White.Opacity(0.25f),
Radius = shape.ShapeSize / 4,
}
}
};
}
}
}
@@ -0,0 +1,55 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Shape.Objects.Drawables.Pieces
{
public class ShapeSquare : Container
{
private BaseShape shape;
private Container box;
public ShapeSquare(BaseShape Shape)
{
shape = Shape;
}
protected override void LoadComplete()
{
base.LoadComplete();
Children = new Drawable[]
{
box = new Container
{
Size = new Vector2(shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
BorderThickness = shape.ShapeSize / 6,
Depth = -2,
BorderColour = Color4.White,
CornerRadius = shape.ShapeSize / 8,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 0,
RelativeSizeAxes = Axes.Both
},
},
EdgeEffect = new EdgeEffectParameters
{
Hollow = true,
Type = EdgeEffectType.Shadow,
Colour = Color4.White.Opacity(0.25f),
Radius = shape.ShapeSize / 4,
}
}
};
}
}
}
@@ -0,0 +1,35 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Shape.Objects.Drawables.Pieces
{
public class ShapeTriangle : Container
{
private BaseShape shape;
private Triangle triangle;
public ShapeTriangle(BaseShape Shape)
{
shape = Shape;
}
protected override void LoadComplete()
{
base.LoadComplete();
Children = new Drawable[]
{
triangle = new Triangle
{
Size = new Vector2(shape.ShapeSize),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Depth = -2,
}
};
}
}
}
@@ -0,0 +1,101 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Shape.Objects.Drawables.Pieces
{
public class ShapeX : Container
{
private BaseShape shape;
private Container line1;
private Container line1Glow;
private Container line2;
public ShapeX(BaseShape Shape)
{
shape = Shape;
}
protected override void LoadComplete()
{
base.LoadComplete();
Children = new Drawable[]
{
line1 = new Container
{
Rotation = 360 - 45,
Size = new Vector2(shape.ShapeSize / 6 , shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Depth = -2,
Colour = Color4.White,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 1,
RelativeSizeAxes = Axes.Both
},
},
},
line2 = new Container
{
Rotation = 45,
Size = new Vector2(shape.ShapeSize / 6 , shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Depth = -1,
Colour = Color4.White,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 1,
RelativeSizeAxes = Axes.Both
},
},
EdgeEffect = new EdgeEffectParameters
{
Hollow = true,
Type = EdgeEffectType.Shadow,
Colour = Color4.White.Opacity(0.25f),
Radius = shape.ShapeSize / 4,
}
},
line1Glow = new Container
{
Rotation = 360 - 45,
Size = new Vector2(shape.ShapeSize / 6 , shape.ShapeSize),
Masking = true,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Depth = 0,
Colour = Color4.White,
Children = new[]
{
new Box
{
AlwaysPresent = true,
Alpha = 0,
RelativeSizeAxes = Axes.Both
},
},
EdgeEffect = new EdgeEffectParameters
{
Hollow = true,
Type = EdgeEffectType.Shadow,
Colour = Color4.White.Opacity(0.25f),
Radius = shape.ShapeSize / 4,
}
},
};
}
}
}
@@ -0,0 +1,8 @@
namespace osu.Game.Rulesets.Shape.Objects
{
public enum HitObjectType
{
Shape,
Arrow,
}
}
@@ -0,0 +1,48 @@
using osu.Game.Rulesets.Objects;
using OpenTK;
using osu.Game.Rulesets.Shape.Objects.Drawables;
using OpenTK.Graphics;
using osu.Game.Database;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps;
namespace osu.Game.Rulesets.Shape.Objects
{
public abstract class ShapeHitObject : HitObject
{
public const double HitboxSize = 8;
public float BPM;
public Vector2 Position { get; set; }
public Vector2 StackedPosition => Position + StackOffset;
public virtual Vector2 EndPosition => Position;
public Vector2 StackedEndPosition => EndPosition + StackOffset;
public virtual int StackHeight { get; set; }
public Vector2 StackOffset => new Vector2(0,0);
public double Radius => HitboxSize * Scale;
public float Scale { get; set; } = 1;
public abstract HitObjectType Type { get; }
public Color4 ComboColour { get; set; }
public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; }
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
}
}
@@ -0,0 +1,120 @@
using OpenTK;
using System;
using System.Diagnostics;
using System.Linq;
namespace osu.Game.Rulesets.Shape.Objects
{
internal class ShapeHitObjectDifficulty
{
/// <summary>
/// Factor by how much speed / aim strain decays per second.
/// </summary>
/// <remarks>
/// These values are results of tweaking a lot and taking into account general feedback.
/// Opinionated observation: Speed is easier to maintain than accurate jumps.
/// </remarks>
internal static readonly double[] DECAY_BASE = { 0.3, 0.15 };
/// <summary>
/// Pseudo threshold values to distinguish between "singles" and "streams"
/// </summary>
/// <remarks>
/// Of course the border can not be defined clearly, therefore the algorithm has a smooth transition between those values.
/// They also are based on tweaking and general feedback.
/// </remarks>
private const double stream_spacing_threshold = 110,
single_spacing_threshold = 125;
/// <summary>
/// Scaling values for weightings to keep aim and speed difficulty in balance.
/// </summary>
/// <remarks>
/// Found from testing a very large map pool (containing all ranked maps) and keeping the average values the same.
/// </remarks>
private static readonly double[] spacing_weight_scaling = { 1400, 26.25 };
/// <summary>
/// Almost the normed diameter of a hitbox (104 osu pixel). That is -after- position transforming.
/// </summary>
private const double almost_diameter = 90;
internal ShapeHitObject BaseHitObject;
internal double[] Strains = { 1, 1 };
internal int MaxCombo = 1;
private float scalingFactor;
private Vector2 startPosition = new Vector2(0);
private Vector2 endPosition;
internal ShapeHitObjectDifficulty(ShapeHitObject baseHitObject)
{
BaseHitObject = baseHitObject;
float hitboxRadius = baseHitObject.Scale * 64;
// We will scale everything by this factor, so we can assume a uniform HitboxSize among beatmaps.
scalingFactor = 52.0f / hitboxRadius;
if (hitboxRadius < 4)
{
float smallHitboxBonus = Math.Min(30.0f - hitboxRadius, 5.0f) / 50.0f;
scalingFactor *= 1.0f + smallHitboxBonus;
}
else
endPosition = startPosition;
}
internal void CalculateStrains(ShapeHitObjectDifficulty previousHitObject, double timeRate)
{
calculateSpecificStrain(previousHitObject, ShapeDifficultyCalculator.DifficultyType.Speed, timeRate);
calculateSpecificStrain(previousHitObject, ShapeDifficultyCalculator.DifficultyType.Aim, timeRate);
}
// Caution: The subjective values are strong with this one
private static double spacingWeight(double distance, ShapeDifficultyCalculator.DifficultyType type)
{
switch (type)
{
case ShapeDifficultyCalculator.DifficultyType.Speed:
if (distance > single_spacing_threshold)
return 2.5;
else if (distance > stream_spacing_threshold)
return 1.6 + 0.9 * (distance - stream_spacing_threshold) / (single_spacing_threshold - stream_spacing_threshold);
else if (distance > almost_diameter)
return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter);
else if (distance > almost_diameter / 2)
return 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2);
else
return 0.95;
case ShapeDifficultyCalculator.DifficultyType.Aim:
return Math.Pow(distance, 0.99);
}
Debug.Assert(false, "Invalid Shape difficulty hit object type.");
return 0;
}
private void calculateSpecificStrain(ShapeHitObjectDifficulty previousHitObject, ShapeDifficultyCalculator.DifficultyType type, double timeRate)
{
double addition = 0;
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
double decay = Math.Pow(DECAY_BASE[(int)type], timeElapsed / 1000);
addition = spacingWeight(DistanceTo(previousHitObject), type) * spacing_weight_scaling[(int)type];
// You will never find maps that require this amongst ranked maps.
addition /= Math.Max(timeElapsed, 50);
Strains[(int)type] = previousHitObject.Strains[(int)type] * decay + addition;
}
internal double DistanceTo(ShapeHitObjectDifficulty other)
{
// Scale the distance by hitbox size.
return (startPosition - other.endPosition).Length * scalingFactor;
}
}
}
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Vitaru.Objects.Characters;
namespace osu.Game.Modes.Vitaru.Objects
{
internal class VitaruConverter : HitObjectConverter<VitaruHitObject>
{
public override List<VitaruHitObject> Convert(Beatmap beatmap)
{
List<VitaruHitObject> output = new List<VitaruHitObject>();
foreach (HitObject i in beatmap.HitObjects)
{
VitaruHitObject h = i as VitaruHitObject;
if (h == null)
{
OsuHitObject o = i as OsuHitObject;
if (o == null) throw new HitObjectConvertException(@"Vitaru", i);
h = new Enemy();
}
output.Add(h);
}
return output;
}
}
}
+25
View File
@@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>
@@ -0,0 +1,38 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
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("osu.Game.Modes.Vitaru")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("osu.Game.Modes.Vitaru")]
[assembly: AssemblyCopyright("Copyright © 2016 - 2017")]
[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("58f6c80c-1253-4a0e-a465-b8c85ebeadf3")]
// 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,92 @@
using System.Collections.Generic;
using osu.Framework.Extensions;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Shape.Judgements;
using osu.Game.Rulesets.Shape.Objects;
using osu.Game.Rulesets.Shape.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Shape.Scoring
{
internal class ShapeScoreProcessor : ScoreProcessor<ShapeHitObject>
{
public ShapeScoreProcessor()
{
}
public ShapeScoreProcessor(RulesetContainer<ShapeHitObject> rulesetContainer)
: base(rulesetContainer)
{
}
protected override void Reset(bool storeResults)
{
base.Reset(storeResults);
Health.Value = 1;
TotalScore.Value = 0;
scoreResultCounts.Clear();
comboResultCounts.Clear();
}
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>();
private float hpDrainRate;
private int totalAccurateJudgements;
public override void PopulateScore(Score score)
{
base.PopulateScore(score);
score.Statistics[HitResult.Perfect] = scoreResultCounts.GetOrDefault(HitResult.Perfect);
score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great);
score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good);
score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh);
score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss);
}
protected override void SimulateAutoplay(Beatmap<ShapeHitObject> beatmap)
{
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
totalAccurateJudgements = beatmap.HitObjects.Count;
foreach (var unused in beatmap.HitObjects)
{
AddJudgement(new ShapeJudgement { Result = HitResult.Perfect });
}
}
protected override void OnNewJudgement(Judgement judgement)
{
base.OnNewJudgement(judgement);
var shapeJudgement = (ShapeJudgement)judgement;
if (judgement != null)
{
if (judgement.Result != HitResult.None)
{
scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1;
comboResultCounts[shapeJudgement.Combo] = comboResultCounts.GetOrDefault(shapeJudgement.Combo) + 1;
}
switch (judgement.Result)
{
case HitResult.Great:
Health.Value = Health.Value + 0.05f;
break;
case HitResult.Miss:
Health.Value = Health.Value - 0.1f;
break;
}
}
}
}
}
@@ -0,0 +1,23 @@
using osu.Framework.Configuration;
using osu.Framework.Platform;
namespace osu.Game.Rulesets.Shape.Settings
{
public class ShapeConfigManager : IniConfigManager<ShapeSetting>
{
protected override string Filename => @"shape.ini";
public ShapeConfigManager(Storage storage) : base(storage) { }
protected override void InitialiseDefaults()
{
Set(ShapeSetting.Skin, "default");
}
}
public enum ShapeSetting
{
Skin
}
}
@@ -0,0 +1,44 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Platform;
using osu.Game.Overlays.Settings;
using Symcol.Rulesets.Core;
using Symcol.Rulesets.Core.Wiki;
using osu.Game.Rulesets.Shape.Wiki;
using Symcol.Rulesets.Core.Multiplayer.Screens;
using osu.Game.Rulesets.Shape.Multi;
namespace osu.Game.Rulesets.Shape.Settings
{
public class ShapeSettings : SymcolSettingsSubsection
{
protected override string Header => "shape!";
public override WikiOverlay Wiki => shapeWiki;
private readonly ShapeWikiOverlay shapeWiki = new ShapeWikiOverlay();
public override RulesetLobbyItem RulesetLobbyItem => shapeLobby;
private readonly ShapeLobbyItem shapeLobby = new ShapeLobbyItem();
public static ShapeConfigManager ShapeConfigManager;
[BackgroundDependencyLoader]
private void load(GameHost host, Storage storage)
{
ShapeConfigManager = new ShapeConfigManager(host.Storage);
Storage skinsStorage = storage.GetStorageForDirectory("Skins");
Children = new Drawable[]
{
new SettingsButton
{
Text = "Open In-game Wiki",
Action = shapeWiki.Show
}
};
}
}
}
@@ -0,0 +1,136 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Shape.Objects;
using osu.Game.Rulesets.Shape.Beatmaps;
using osu.Game.Rulesets.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Shape
{
public class ShapeDifficultyCalculator : DifficultyCalculator<ShapeHitObject>
{
private const double star_scaling_factor = 0.0675;
private const double extreme_scaling_factor = 0.5;
internal List<ShapeHitObjectDifficulty> DifficultyHitObjects = new List<ShapeHitObjectDifficulty>();
public ShapeDifficultyCalculator(Beatmap beatmap, Mod[] mods) : base(beatmap, mods) { }
public ShapeDifficultyCalculator(Beatmap beatmap) : base(beatmap) { }
protected override void PreprocessHitObjects()
{
foreach (var h in Beatmap.HitObjects) { }
}
protected override BeatmapConverter<ShapeHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ShapeBeatmapConverter();
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
DifficultyHitObjects.Clear();
foreach (var hitObject in Beatmap.HitObjects)
DifficultyHitObjects.Add(new ShapeHitObjectDifficulty(hitObject));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
DifficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
if (!CalculateStrainValues()) return 0;
double speedDifficulty = (CalculateDifficulty(DifficultyType.Speed) * 0.75f);
double aimDifficulty = (CalculateDifficulty(DifficultyType.Aim) * 1.5f);
double speedStars = Math.Sqrt(speedDifficulty) * star_scaling_factor;
double aimStars = Math.Sqrt(aimDifficulty) * star_scaling_factor;
double starRating = speedStars + aimStars + Math.Abs(speedStars - aimStars) * extreme_scaling_factor;
return starRating;
}
protected bool CalculateStrainValues()
{
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
using (List<ShapeHitObjectDifficulty>.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator())
{
if (!hitObjectsEnumerator.MoveNext()) return false;
ShapeHitObjectDifficulty current = hitObjectsEnumerator.Current;
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
current = next;
}
return true;
}
}
protected const double STRAIN_STEP = 200;
protected const double DECAY_WEIGHT = 0.75;
protected double CalculateDifficulty(DifficultyType type)
{
double actualStrainStep = STRAIN_STEP * TimeRate;
List<double> highestStrains = new List<double>();
double intervalEndTime = actualStrainStep;
double maximumStrain = 0;
ShapeHitObjectDifficulty previousHitObject = null;
foreach (ShapeHitObjectDifficulty hitObject in DifficultyHitObjects)
{
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
{
highestStrains.Add(maximumStrain);
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
// until the beginning of the next interval.
if (previousHitObject == null)
{
maximumStrain = 0;
}
else
{
double decay = Math.Pow(ShapeHitObjectDifficulty.DECAY_BASE[(int)type], (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
maximumStrain = previousHitObject.Strains[(int)type] * decay;
}
// Go to the next time interval
intervalEndTime += actualStrainStep;
}
// Obtain maximum strain
maximumStrain = Math.Max(hitObject.Strains[(int)type], maximumStrain);
previousHitObject = hitObject;
}
// Build the weighted sum over the highest strains for each interval
double difficulty = 0;
double weight = 1;
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
foreach (double strain in highestStrains)
{
difficulty += weight * strain;
weight *= DECAY_WEIGHT;
}
return difficulty;
}
public enum DifficultyType
{
Speed = 0,
Aim,
};
}
}
@@ -0,0 +1,34 @@
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
using System.ComponentModel;
namespace osu.Game.Rulesets.Shape
{
class ShapeInputManager : RulesetInputManager<ShapeAction>
{
public ShapeInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique)
{
}
}
public enum ShapeAction
{
//Movement
[Description("West Left")]
WestLeftButton,
[Description("West Right")]
WestRightButton,
[Description("East Left")]
EastLeftButton,
[Description("East Right")]
EastRightButton,
[Description("North Left")]
NorthLeftButton,
[Description("North Right")]
NorthRightButton,
[Description("South Left")]
SouthLeftButton,
[Description("South Right")]
SouthRightButton,
}
}
+110
View File
@@ -0,0 +1,110 @@
using System.Collections.Generic;
using osu.Game.Rulesets.UI;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Shape.Mods;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Framework.Audio;
using osu.Framework.Input.Bindings;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Shape.Settings;
namespace osu.Game.Rulesets.Shape
{
public class ShapeRuleset : Ruleset
{
public override Drawable CreateIcon() => new Sprite { Texture = ShapeTextures.Get("icon") };
public static ResourceStore<byte[]> ShapeResources;
public static TextureStore ShapeTextures;
public static AudioManager ShapeClassicAudio;
public ShapeRuleset(RulesetInfo rulesetInfo)
: base(rulesetInfo)
{
ShapeResources = new ResourceStore<byte[]>();
ShapeResources.AddStore(new NamespacedResourceStore<byte[]>(new DllResourceStore("osu.Game.Rulesets.Shape.dll"), ("Assets")));
ShapeResources.AddStore(new DllResourceStore("osu.Game.Rulesets.Shape.dll"));
ShapeTextures = new TextureStore(new RawTextureLoaderStore(new NamespacedResourceStore<byte[]>(ShapeResources, @"Textures")));
ShapeTextures.AddStore(new RawTextureLoaderStore(new OnlineStore()));
}
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ShapeRulesetContainer(this, beatmap, isForCurrentRuleset);
public override int? LegacyID => 5;
public override string Description => "shape!";
public override string ShortName => "shape";
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ShapeDifficultyCalculator(beatmap, mods);
public override SettingsSubsection CreateSettings() => new ShapeSettings();
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(InputKey.A, ShapeAction.EastLeftButton),
new KeyBinding(InputKey.S, ShapeAction.WestLeftButton),
new KeyBinding(InputKey.D, ShapeAction.NorthLeftButton),
new KeyBinding(InputKey.F, ShapeAction.SouthLeftButton),
new KeyBinding(InputKey.J, ShapeAction.SouthRightButton),
new KeyBinding(InputKey.K, ShapeAction.NorthRightButton),
new KeyBinding(InputKey.L, ShapeAction.WestRightButton),
new KeyBinding(InputKey.Semicolon, ShapeAction.EastRightButton),
};
public override IEnumerable<Mod> GetModsFor(ModType type)
{
switch (type)
{
case ModType.DifficultyReduction:
return new Mod[]
{
new ShapeModEasy(),
new ShapeModNoFail(),
new MultiMod
{
Mods = new Mod[]
{
new ShapeModHalfTime(),
new ShapeModDaycore(),
},
},
};
case ModType.DifficultyIncrease:
return new Mod[]
{
new ShapeModHardRock(),
new ShapeModSuddenDeath(),
new MultiMod
{
Mods = new Mod[]
{
new ShapeModDoubleTime(),
new ShapeModNightcore(),
},
},
new MultiMod
{
Mods = new Mod[]
{
new ShapeModHidden(),
new ShapeModFlashlight(),
},
},
};
case ModType.Special:
return new Mod[]
{
new ShapeRelax()
};
default : return new Mod[] { };
}
}
}
}
@@ -0,0 +1,83 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.Shape.Objects;
using osu.Game.Rulesets.Shape.Objects.Drawables;
using osu.Game.Rulesets.UI;
using OpenTK;
using osu.Game.Rulesets.Shape.Judgements;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Shape.UI
{
public class ShapePlayfield : Playfield
{
private Container shapePlayfield;
private readonly Container judgementLayer;
//public override bool ProvidingUserCursor => false;
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
public override Vector2 Size
{
get
{
var parentSize = Parent.DrawSize;
var aspectSize = parentSize.X * 0.75f < parentSize.Y ? new Vector2(parentSize.X, parentSize.X * 0.75f) : new Vector2(parentSize.Y * 4f / 3f, parentSize.Y);
return new Vector2(aspectSize.X / parentSize.X, aspectSize.Y / parentSize.Y) * base.Size;
}
}
public ShapePlayfield() : base(BASE_SIZE.X)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
AddRange(new Drawable[]
{
shapePlayfield = new Container
{
RelativeSizeAxes = Axes.Both,
Depth = -3,
},
judgementLayer = new Container
{
RelativeSizeAxes = Axes.Both,
Depth = -2,
},
//Will need custom UI like vitaru does it
});
}
public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
h.OnJudgement += onJudgement;
IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null)
shapePlayfield.Add(c.ProxiedLayer.CreateProxy());
base.Add(h);
}
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
var shapeJudgement = (ShapeJudgement)judgement;
var shapeObject = (ShapeHitObject)judgedObject.HitObject;
DrawableShapeJudgement explosion = new DrawableShapeJudgement(shapeJudgement, judgedObject)
{
Scale = new Vector2(0.5f),
Alpha = 0.5f,
Origin = Anchor.Centre,
Position = judgedObject.Position,
};
judgementLayer.Add(explosion);
}
}
}
@@ -0,0 +1,44 @@
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Shape.Judgements;
using osu.Game.Rulesets.Shape.Objects;
using osu.Game.Rulesets.Shape.Objects.Drawables;
using osu.Game.Rulesets.Shape.Beatmaps;
using osu.Game.Rulesets.Shape.UI;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using OpenTK;
using osu.Game.Rulesets.Shape.Scoring;
using osu.Framework.Input;
using System;
namespace osu.Game.Rulesets.Shape
{
internal class ShapeRulesetContainer : RulesetContainer<ShapeHitObject>
{
public ShapeRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
{
}
public override ScoreProcessor CreateScoreProcessor() => new ShapeScoreProcessor(this);
protected override BeatmapConverter<ShapeHitObject> CreateBeatmapConverter() => new ShapeBeatmapConverter();
protected override BeatmapProcessor<ShapeHitObject> CreateBeatmapProcessor() => new ShapeBeatmapProcessor();
protected override Playfield CreatePlayfield() => new ShapePlayfield();
public override PassThroughInputManager CreateInputManager() => new ShapeInputManager(Ruleset.RulesetInfo);
protected override DrawableHitObject<ShapeHitObject> GetVisualRepresentation(ShapeHitObject h)
{
var shape = h as BaseShape;
if (shape != null)
return new DrawableBaseShape(shape);
return null;
}
protected override Vector2 GetAspectAdjustedSize() => new Vector2(0.75f);
}
}
@@ -0,0 +1,9 @@
using Symcol.Rulesets.Core.Wiki;
namespace osu.Game.Rulesets.Shape.Wiki.Sections
{
public class Gameplay : WikiSection
{
public override string Title => "Gameplay";
}
}
@@ -0,0 +1,26 @@
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Users;
using Symcol.Rulesets.Core.Wiki;
namespace osu.Game.Rulesets.Shape.Wiki
{
public class ShapeWikiHeader : WikiHeader
{
protected override Texture RulesetIcon => ShapeRuleset.ShapeTextures.Get("icon");
protected override string RulesetName => "shape";
protected override string RulesetDescription => "shape! is a 3rd party ruleset developed for osu!lazer.";
protected override string RulesetUrl => $@"https://github.com/Symcol/osu/tree/symcol/osu.Game.Rulesets.Shape";
protected override User Creator => new User
{
Username = "Shawdooow",
Id = 7726082
};
protected override string DiscordInvite => $@"https://discord.gg/JvS5cxA";
}
}
@@ -0,0 +1,15 @@
using osu.Game.Rulesets.Shape.Wiki.Sections;
using Symcol.Rulesets.Core.Wiki;
namespace osu.Game.Rulesets.Shape.Wiki
{
public class ShapeWikiOverlay : WikiOverlay
{
protected override WikiHeader Header => new ShapeWikiHeader();
protected override WikiSection[] Sections => new WikiSection[]
{
new Gameplay()
};
}
}
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" 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>{BAF6D7B6-5E48-4278-AB81-F079AF640E31}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Shape</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Shape</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>..\osu.Game\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>..\osu.Game\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, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<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="Beatmaps\ShapeBeatmapConverter.cs" />
<Compile Include="Beatmaps\ShapeBeatmapProcessor.cs" />
<Compile Include="Judgements\ShapeJudgement.cs" />
<Compile Include="Multi\ShapeLobbyItem.cs" />
<Compile Include="Multi\ShapeLobbyScreen.cs" />
<Compile Include="Multi\ShapeMatchScreen.cs" />
<Compile Include="Objects\Drawables\Pieces\Arrow.cs" />
<Compile Include="Objects\Drawables\Pieces\Circle.cs" />
<Compile Include="Objects\Drawables\Pieces\Square.cs" />
<Compile Include="Objects\Drawables\Pieces\Triangle.cs" />
<Compile Include="Objects\Drawables\Pieces\X.cs" />
<Compile Include="Objects\BaseShape.cs" />
<Compile Include="Objects\Drawables\DrawableBaseShape.cs" />
<Compile Include="Objects\Drawables\DrawableShapeJudgement.cs" />
<Compile Include="Mods\ShapeMod.cs" />
<Compile Include="Objects\Drawables\DrawableShapeHitObject.cs" />
<Compile Include="Objects\Drawables\Pieces\BaseDial.cs" />
<Compile Include="Objects\HitObjectType.cs" />
<Compile Include="Objects\ShapeHitObjectDifficulty.cs" />
<Compile Include="Objects\ShapeHitObject.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Settings\ShapeConfigManager.cs" />
<Compile Include="Settings\ShapeSettings.cs" />
<Compile Include="ShapeDifficultyCalculator.cs" />
<Compile Include="UI\ShapeRulsesetContainer.cs" />
<Compile Include="Scoring\ShapeScoreProcessor.cs" />
<Compile Include="UI\ShapePlayfield.cs" />
<Compile Include="ShapeRuleset.cs" />
<Compile Include="ShapeInputManager.cs" />
<Compile Include="Wiki\Sections\Gamemodes.cs" />
<Compile Include="Wiki\ShapeWikiHeader.cs" />
<Compile Include="Wiki\ShapeWikiOverlay.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</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>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project>
<Name>osu.Game</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>
<ItemGroup>
<Folder Include="Assets\Audio\" />
<Folder Include="Assets\Font\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\Textures\icon.png" />
</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">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="3.8.1" targetFramework="net461" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
</packages>
@@ -0,0 +1,5 @@
info face="vitaru!font" size=100 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=4 padding=0,0,0,0 spacing=4,4 outline=0
common lineHeight=100 base=83 scaleW=1024 scaleH=1024 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
page id=0 file="vitaruFont_0.png"
chars count=1
char id=57419 x=495 y=537 width=88 height=89 xoffset=0 yoffset=8 xadvance=89 page=0 chnl=15
Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

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