mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 21:13:39 +08:00
Compare commits
32 Commits
2024.312.0
...
0.7.2v
@@ -0,0 +1,80 @@
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK;
|
||||
using Symcol.Core.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.GameObjects
|
||||
{
|
||||
public class SymcolHitbox : SymcolContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// whether we want to do hit detection
|
||||
/// </summary>
|
||||
public int Team { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// whether we want to do hit detection
|
||||
/// </summary>
|
||||
public bool HitDetection { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// the shape of this object (used for hit detection)
|
||||
/// </summary>
|
||||
public Shape Shape { get; }
|
||||
|
||||
public SymcolHitbox(Vector2 size, Shape shape = Shape.Circle)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Shape = shape;
|
||||
Size = size;
|
||||
|
||||
if (Shape == Shape.Circle)
|
||||
Child = new SymcolContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CornerRadius = Width / 2
|
||||
};
|
||||
else if (Shape == Shape.Rectangle)
|
||||
Child = new SymcolContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
}
|
||||
|
||||
public bool HitDetect(SymcolHitbox hitbox1, SymcolHitbox hitbox2)
|
||||
{
|
||||
if (hitbox1.HitDetection && hitbox2.HitDetection && hitbox1.Team != hitbox2.Team)
|
||||
{
|
||||
if (hitbox1.Shape == Shape.Circle && hitbox2.Shape == Shape.Circle)
|
||||
{
|
||||
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
else if (hitbox1.Shape == Shape.Circle && hitbox2.Shape == Shape.Rectangle || hitbox1.Shape == Shape.Rectangle && hitbox2.Shape == Shape.Circle)
|
||||
{
|
||||
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
else if (hitbox1.Shape == Shape.Rectangle && hitbox2.Shape == Shape.Rectangle)
|
||||
{
|
||||
if (hitbox1.ScreenSpaceDrawQuad.AABB.IntersectsWith(hitbox2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
else if (hitbox1.Shape == Shape.Complex || hitbox2.Shape == Shape.Complex)
|
||||
foreach (SymcolContainer child1 in hitbox1.Children)
|
||||
foreach (SymcolContainer child2 in hitbox2.Children)
|
||||
if (child1.ScreenSpaceDrawQuad.AABB.IntersectsWith(child2.ScreenSpaceDrawQuad.AABB))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Shape
|
||||
{
|
||||
Circle,
|
||||
Rectangle,
|
||||
Complex
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Will support base eden game functions (if we come up with any)
|
||||
/// </summary>
|
||||
public class SymcolClickableContainer : ClickableContainer
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Will support base eden game functions (if we come up with any)
|
||||
/// </summary>
|
||||
public class SymcolContainer : Container
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
public class SymcolDialContainer : CircularContainer
|
||||
{
|
||||
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
private Vector2 mousePosition;
|
||||
|
||||
private float lastAngle;
|
||||
private float currentRotation;
|
||||
public float RotationAbsolute;
|
||||
|
||||
private int completeTick;
|
||||
|
||||
private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360));
|
||||
|
||||
private bool rotationTransferred;
|
||||
|
||||
protected override bool OnMouseMove(InputState state)
|
||||
{
|
||||
mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
|
||||
return base.OnMouseMove(state);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
|
||||
|
||||
|
||||
if (!rotationTransferred)
|
||||
{
|
||||
currentRotation = Rotation * 2;
|
||||
rotationTransferred = true;
|
||||
}
|
||||
|
||||
if (thisAngle - lastAngle > 180)
|
||||
lastAngle += 360;
|
||||
else if (lastAngle - thisAngle > 180)
|
||||
lastAngle -= 360;
|
||||
|
||||
currentRotation += thisAngle - lastAngle;
|
||||
RotationAbsolute += Math.Abs(thisAngle - lastAngle);
|
||||
|
||||
lastAngle = thisAngle;
|
||||
|
||||
foreach(Drawable drawable in Children)
|
||||
drawable.RotateTo(currentRotation / 2, 200, Easing.OutExpo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
|
||||
namespace Symcol.Core.Graphics.Containers
|
||||
{
|
||||
public class SymcolDragContainer : SymcolContainer
|
||||
{
|
||||
protected override bool OnDragStart(InputState state) => true;
|
||||
|
||||
public bool AllowLeftClickDrag { get; set; } = true;
|
||||
|
||||
private bool drag;
|
||||
|
||||
private Vector2 startPosition;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
startPosition = Position;
|
||||
|
||||
if (args.Button == MouseButton.Left && AllowLeftClickDrag || args.Button == MouseButton.Right)
|
||||
drag = true;
|
||||
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
if (drag)
|
||||
Position = startPosition + state.Mouse.Position - state.Mouse.PositionMouseDown.GetValueOrDefault();
|
||||
|
||||
return base.OnDrag(state);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
if (args.Button == MouseButton.Left && AllowLeftClickDrag || args.Button == MouseButton.Right)
|
||||
drag = false;
|
||||
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using Symcol.Core.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.UserInterface
|
||||
{
|
||||
/// <summary>
|
||||
/// just a Button with a sprite
|
||||
/// </summary>
|
||||
public class SpriteButton : SymcolClickableContainer
|
||||
{
|
||||
private readonly string textureName;
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return spriteText?.Text; }
|
||||
set
|
||||
{
|
||||
if (spriteText != null)
|
||||
spriteText.Text = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Sprite sprite;
|
||||
private readonly SpriteText spriteText;
|
||||
|
||||
public SpriteButton(string textureName)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
this.textureName = textureName;
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
sprite = new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill
|
||||
},
|
||||
spriteText = new SpriteText
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
sprite.Texture = textures.Get(textureName);
|
||||
}
|
||||
|
||||
protected override bool OnClick(InputState state)
|
||||
{
|
||||
if (Enabled.Value)
|
||||
{
|
||||
var flash = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
};
|
||||
|
||||
Add(flash);
|
||||
|
||||
flash.Blending = BlendingMode.Additive;
|
||||
flash.FadeOut(200);
|
||||
flash.Expire();
|
||||
}
|
||||
|
||||
return base.OnClick(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Input;
|
||||
using Symcol.Core.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Core.Graphics.UserInterface
|
||||
{
|
||||
public class SymcolWindow : SymcolContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Put all your stuff in this
|
||||
/// </summary>
|
||||
public SymcolContainer WindowContent { get; set; }
|
||||
public SpriteText WindowTitle;
|
||||
|
||||
private readonly SymcolContainer topBar;
|
||||
private readonly SymcolClickableContainer minimize;
|
||||
|
||||
public SymcolWindow(Vector2 size)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
CornerRadius = 6;
|
||||
Masking = true;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
topBar = new SymcolContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 20,
|
||||
Width = size.X,
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
},
|
||||
WindowTitle = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
TextSize = 18
|
||||
},
|
||||
new SymcolClickableContainer
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 30,
|
||||
Action = Close,
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.Red,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
}
|
||||
},
|
||||
minimize = new SymcolClickableContainer
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = 30,
|
||||
Position = new Vector2(-30, 0),
|
||||
Action = Minimize,
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.White,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.5f
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
WindowContent = new SymcolContainer
|
||||
{
|
||||
Size = size,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
}
|
||||
};
|
||||
|
||||
WindowContent.Position = new Vector2(0, topBar.Height);
|
||||
}
|
||||
|
||||
protected void Close()
|
||||
{
|
||||
this.FadeOut(200);
|
||||
}
|
||||
|
||||
protected void Open()
|
||||
{
|
||||
this.FadeIn(200);
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
if (Alpha > 0)
|
||||
this.FadeOut(200);
|
||||
else
|
||||
this.FadeIn(200);
|
||||
}
|
||||
|
||||
protected override bool OnDragStart(InputState state) => true;
|
||||
|
||||
private bool drag;
|
||||
|
||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
if (args.Button == MouseButton.Left)
|
||||
drag = true;
|
||||
|
||||
return base.OnMouseDown(state, args);
|
||||
}
|
||||
|
||||
protected override bool OnDrag(InputState state)
|
||||
{
|
||||
if (drag)
|
||||
Position += state.Mouse.Delta;
|
||||
|
||||
return base.OnDrag(state);
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
if (args.Button == MouseButton.Left)
|
||||
drag = false;
|
||||
|
||||
return base.OnMouseUp(state, args);
|
||||
}
|
||||
|
||||
public void Maximize()
|
||||
{
|
||||
WindowContent.FadeIn(200);
|
||||
WindowContent.ScaleTo(Vector2.One, 200);
|
||||
minimize.Action = Minimize;
|
||||
}
|
||||
|
||||
public void Minimize()
|
||||
{
|
||||
WindowContent.FadeOut(200);
|
||||
WindowContent.ScaleTo(new Vector2(1, 0), 200);
|
||||
minimize.Action = Maximize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class BasicPacket : Packet
|
||||
{
|
||||
/// <summary>
|
||||
/// Ask host if we can connect
|
||||
/// </summary>
|
||||
public bool Connect;
|
||||
|
||||
/// <summary>
|
||||
/// Tell the host we are breaking up
|
||||
/// </summary>
|
||||
public bool Disconnect;
|
||||
|
||||
/// <summary>
|
||||
/// Testing Connection
|
||||
/// </summary>
|
||||
public bool Test;
|
||||
|
||||
/// <summary>
|
||||
/// Send a force exit to others
|
||||
/// </summary>
|
||||
public bool Abort;
|
||||
|
||||
/// <summary>
|
||||
/// PreLoad the game
|
||||
/// </summary>
|
||||
public bool LoadGame;
|
||||
|
||||
/// <summary>
|
||||
/// Request a list of all players from Host
|
||||
/// </summary>
|
||||
public bool RequestPlayerList;
|
||||
|
||||
/// <summary>
|
||||
/// List of players in this match that we should account for
|
||||
/// </summary>
|
||||
public List<ClientInfo> PlayerList = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Tell Host we are PreLoaded
|
||||
/// </summary>
|
||||
public bool Loaded;
|
||||
|
||||
/// <summary>
|
||||
/// Start the game already!
|
||||
/// </summary>
|
||||
public bool StartGame;
|
||||
|
||||
/// <summary>
|
||||
/// Send to host when game started
|
||||
/// </summary>
|
||||
public bool GameStarted;
|
||||
|
||||
public BasicPacket(ClientInfo clientInfo) : base(clientInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// Just a client signature basically
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ClientInfo
|
||||
{
|
||||
public string IP;
|
||||
|
||||
public int Port;
|
||||
|
||||
public int Ping;
|
||||
|
||||
public int ConncetionTryCount;
|
||||
|
||||
public double LastConnectionTime;
|
||||
|
||||
public double StartedTestConnectionTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
public class NetworkingClient
|
||||
{
|
||||
public UdpClient UdpClient;
|
||||
|
||||
public IPEndPoint EndPoint;
|
||||
|
||||
/// <summary>
|
||||
/// if false we only receive
|
||||
/// </summary>
|
||||
public readonly bool Send;
|
||||
|
||||
public readonly int Port;
|
||||
|
||||
public readonly string IP;
|
||||
|
||||
public NetworkingClient(bool send, string ip, int port = 25570)
|
||||
{
|
||||
Port = port;
|
||||
IP = ip;
|
||||
|
||||
if (send)
|
||||
initializeSend();
|
||||
else
|
||||
initializeReceive();
|
||||
}
|
||||
|
||||
private void initializeSend()
|
||||
{
|
||||
UdpClient = new UdpClient(IP, Port);
|
||||
}
|
||||
|
||||
private void initializeReceive()
|
||||
{
|
||||
UdpClient = new UdpClient(Port);
|
||||
EndPoint = new IPEndPoint(IPAddress.Any, Port);
|
||||
}
|
||||
|
||||
private void sendByte(byte[] data)
|
||||
{
|
||||
UdpClient.Send(data, data.Length);
|
||||
}
|
||||
|
||||
private byte[] receiveByte()
|
||||
{
|
||||
return UdpClient.Receive(ref EndPoint);
|
||||
}
|
||||
|
||||
public static int SENTPACKETCOUNT;
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet somewhere
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendPacket(Packet packet)
|
||||
{
|
||||
SENTPACKETCOUNT++;
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
formatter.Serialize(stream, packet);
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
int i = packet.PacketSize;
|
||||
retry:
|
||||
byte[] data = new byte[i];
|
||||
|
||||
try
|
||||
{
|
||||
stream.Read(data, 0, (int)stream.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
i *= 2;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
sendByte(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive a Packet from somewhere
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Packet ReceivePacket(bool force = false)
|
||||
{
|
||||
if (UdpClient.Available > 0 || force)
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
byte[] data = receiveByte();
|
||||
stream.Write(data, 0, data.Length);
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
Packet packet = (Packet)formatter.Deserialize(stream);
|
||||
packet.ClientInfo.IP = EndPoint.Address.ToString();
|
||||
|
||||
return packet;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (UdpClient != null)
|
||||
UdpClient.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,646 @@
|
||||
#define SoloTesting
|
||||
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
//TODO: This NEEDS its own clock to avoid fuckery later on with DoubleTime and HalfTime
|
||||
public class NetworkingClientHandler : Container
|
||||
{
|
||||
//30 Seconds by default
|
||||
protected virtual double TimeOutTime => 30000;
|
||||
|
||||
protected readonly NetworkingClient ReceiveClient;
|
||||
|
||||
protected readonly NetworkingClient SendClient;
|
||||
|
||||
/// <summary>
|
||||
/// Just a client signature basically
|
||||
/// </summary>
|
||||
public ClientInfo ClientInfo;
|
||||
|
||||
/// <summary>
|
||||
/// All Connecting clients
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> ConnectingClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// All Connected clients
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> ConncetedClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Clients waiting in our match
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> InMatchClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Clients loaded and ready to start
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> LoadedClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Clients ingame playing
|
||||
/// </summary>
|
||||
public readonly List<ClientInfo> InGameClients = new List<ClientInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets hit when we get a Packet
|
||||
/// </summary>
|
||||
public Action<Packet> OnPacketReceive;
|
||||
|
||||
/// <summary>
|
||||
/// (Peer) Call this when we connect to a Host (Includes list of connected peers + Host)
|
||||
/// </summary>
|
||||
public Action<List<ClientInfo>> OnConnectedToHost;
|
||||
|
||||
/// <summary>
|
||||
/// (Host) Whenever a new client Connects
|
||||
/// </summary>
|
||||
public Action<ClientInfo> OnClientConnect;
|
||||
|
||||
/// <summary>
|
||||
/// (Host) Whenever a new client Disconnects
|
||||
/// </summary>
|
||||
public Action<ClientInfo> OnClientDisconnect;
|
||||
|
||||
/// <summary>
|
||||
/// (Host/Peer) When a new Client joins the game
|
||||
/// </summary>
|
||||
public Action<ClientInfo> OnClientJoin;
|
||||
|
||||
/// <summary>
|
||||
/// Receive a full player list
|
||||
/// </summary>
|
||||
public Action<List<ClientInfo>> OnReceivePlayerList;
|
||||
|
||||
/// <summary>
|
||||
/// if we are connected and in a match
|
||||
/// </summary>
|
||||
public bool InMatch;
|
||||
|
||||
/// <summary>
|
||||
/// Are we in a game
|
||||
/// </summary>
|
||||
public bool InGame;
|
||||
|
||||
/// <summary>
|
||||
/// Are we loaded and ready to start?
|
||||
/// </summary>
|
||||
public bool Loaded;
|
||||
|
||||
/// <summary>
|
||||
/// Called to leave an in-progress game
|
||||
/// </summary>
|
||||
public Action OnAbort;
|
||||
|
||||
/// <summary>
|
||||
/// Called to load the game (Includes Host)
|
||||
/// </summary>
|
||||
public Action<List<ClientInfo>> OnLoadGame;
|
||||
|
||||
/// <summary>
|
||||
/// Called to start the game once loaded
|
||||
/// </summary>
|
||||
public Action StartGame;
|
||||
|
||||
public readonly ClientType ClientType;
|
||||
|
||||
public NetworkingClientHandler(ClientType type, string ip, int port = 25570, string thisLocalIp = "0.0.0.0")
|
||||
{
|
||||
AlwaysPresent = true;
|
||||
|
||||
ClientType = type;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ClientType.Host:
|
||||
ReceiveClient = new NetworkingClient(false, ip, port);
|
||||
break;
|
||||
case ClientType.Peer:
|
||||
ReceiveClient = new NetworkingClient(false, thisLocalIp, port);
|
||||
SendClient = new NetworkingClient(true, ip, port);
|
||||
break;
|
||||
case ClientType.Server:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Logger.Log("Created a RulesetNetworkingClientHandler", LoggingTarget.Network, LogLevel.Verbose);
|
||||
|
||||
if (ClientInfo == null)
|
||||
ClientInfo = new ClientInfo
|
||||
{
|
||||
Port = port
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (ClientType == ClientType.Peer)
|
||||
ConnectToHost();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
PacketRestart:
|
||||
Packet p = null;
|
||||
|
||||
if (ReceiveClient.UdpClient.Available > 0)
|
||||
p = ReceiveClient.ReceivePacket();
|
||||
|
||||
if (p is BasicPacket packet)
|
||||
{
|
||||
//Hosts
|
||||
if (SendClient == null)
|
||||
{
|
||||
if (packet.Disconnect)
|
||||
{
|
||||
OnClientDisconnect?.Invoke(packet.ClientInfo);
|
||||
foreach (ClientInfo client in ConnectingClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
ConnectingClients.Remove(client);
|
||||
Logger.Log("A Connecting Client has Disconnected", LoggingTarget.Network, LogLevel.Verbose);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in ConncetedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
ConncetedClients.Remove(client);
|
||||
Logger.Log("A Client has Disconnected", LoggingTarget.Network, LogLevel.Verbose);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in InMatchClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
InMatchClients.Remove(client);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in LoadedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
LoadedClients.Remove(client);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in InGameClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
InGameClients.Remove(client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.Connect)
|
||||
{
|
||||
packet.ClientInfo.StartedTestConnectionTime = Time.Current;
|
||||
ConnectingClients.Add(packet.ClientInfo);
|
||||
|
||||
NetworkingClient client = new NetworkingClient(true, packet.ClientInfo.IP, packet.ClientInfo.Port);
|
||||
|
||||
List<ClientInfo> playerList = new List<ClientInfo>
|
||||
{
|
||||
ClientInfo
|
||||
};
|
||||
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
playerList.Add(clientInfo);
|
||||
|
||||
client.SendPacket(new BasicPacket(ClientInfo)
|
||||
{
|
||||
PlayerList = playerList,
|
||||
Connect = true
|
||||
});
|
||||
|
||||
Logger.Log("A Client is Connecting. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.RequestPlayerList)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, packet.ClientInfo.IP, packet.ClientInfo.Port);
|
||||
|
||||
List<ClientInfo> playerList = new List<ClientInfo>
|
||||
{
|
||||
ClientInfo
|
||||
};
|
||||
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
playerList.Add(clientInfo);
|
||||
|
||||
client.SendPacket(new BasicPacket(ClientInfo)
|
||||
{
|
||||
PlayerList = playerList,
|
||||
RequestPlayerList = true
|
||||
});
|
||||
|
||||
Logger.Log("A Client is Connecting. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.Loaded)
|
||||
foreach (ClientInfo client in InMatchClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
Logger.Log("A Client has Loaded and is ready to start", LoggingTarget.Network, LogLevel.Verbose);
|
||||
InMatchClients.Remove(client);
|
||||
LoadedClients.Add(client);
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet.GameStarted)
|
||||
foreach (ClientInfo client in LoadedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
Logger.Log("A Client has started!", LoggingTarget.Network, LogLevel.Verbose);
|
||||
LoadedClients.Remove(client);
|
||||
InGameClients.Add(client);
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet.Test)
|
||||
{
|
||||
foreach (ClientInfo client in ConnectingClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
client.Ping = (int)Time.Current - (int)client.StartedTestConnectionTime;
|
||||
ConnectingClients.Remove(client);
|
||||
ConncetedClients.Add(client);
|
||||
InMatchClients.Add(client);
|
||||
OnClientJoin?.Invoke(client);
|
||||
client.LastConnectionTime = Time.Current;
|
||||
client.ConncetionTryCount = 0;
|
||||
Logger.Log("Successfully connected to a Client! Ping: " + client.Ping, LoggingTarget.Network, LogLevel.Verbose);
|
||||
break;
|
||||
}
|
||||
foreach (ClientInfo client in ConncetedClients)
|
||||
if (client.IP == packet.ClientInfo.IP)
|
||||
{
|
||||
client.Ping = (int)Time.Current - (int)client.StartedTestConnectionTime;
|
||||
client.LastConnectionTime = Time.Current;
|
||||
client.ConncetionTryCount = 0;
|
||||
Logger.Log("Successfully maintained connection to a Client! Ping: " + client.Ping, LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !SoloTesting
|
||||
if (InMatchClients.Count == 0 && LoadedClients.Count > 0 && Loaded && !InGame)
|
||||
SendStartGame();
|
||||
#endif
|
||||
|
||||
//Peers
|
||||
else if (SendClient != null)
|
||||
{
|
||||
if (packet.Connect)
|
||||
{
|
||||
if (!InGame && !InMatch)
|
||||
{
|
||||
InMatch = true;
|
||||
OnConnectedToHost?.Invoke(packet.PlayerList);
|
||||
}
|
||||
Logger.Log("Connected to Host!", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.Test)
|
||||
{
|
||||
SendToHost(new BasicPacket(ClientInfo) { Test = true });
|
||||
Logger.Log("Received connection test info from host, returning. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
if (packet.RequestPlayerList)
|
||||
OnReceivePlayerList?.Invoke(packet.PlayerList);
|
||||
|
||||
if (packet.StartGame)
|
||||
{
|
||||
StartGame?.Invoke();
|
||||
SendToHost(new BasicPacket(ClientInfo) { GameStarted = true });
|
||||
InGame = true;
|
||||
}
|
||||
|
||||
if (packet.Abort)
|
||||
{
|
||||
OnAbort?.Invoke();
|
||||
InGame = false;
|
||||
Loaded = false;
|
||||
}
|
||||
|
||||
if (packet.LoadGame)
|
||||
{
|
||||
Logger.Log("Received instructions to LoadGame for " + packet.PlayerList.Count + " players", LoggingTarget.Network, LogLevel.Verbose);
|
||||
OnLoadGame?.Invoke(packet.PlayerList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SoloTesting
|
||||
if (Loaded && !InGame)
|
||||
SendStartGame();
|
||||
#endif
|
||||
|
||||
if (p != null)
|
||||
OnPacketReceive?.Invoke(p);
|
||||
|
||||
if (ReceiveClient.UdpClient.Available > 0)
|
||||
goto PacketRestart;
|
||||
|
||||
foreach (ClientInfo client in ConnectingClients)
|
||||
{
|
||||
if (client.LastConnectionTime + TimeOutTime / 10 <= Time.Current && client.ConncetionTryCount == 0)
|
||||
{
|
||||
client.StartedTestConnectionTime = Time.Current;
|
||||
TestConnection(client);
|
||||
}
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 6 <= Time.Current && client.ConncetionTryCount == 1)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 3 <= Time.Current && client.ConncetionTryCount == 2)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.StartedTestConnectionTime + TimeOutTime <= Time.Current)
|
||||
{
|
||||
ConnectingClients.Remove(client);
|
||||
Logger.Log("Connection to a connecting client lost! - " + client.IP + ":" + client.Port, LoggingTarget.Network, LogLevel.Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ClientInfo client in ConncetedClients)
|
||||
{
|
||||
if (client.LastConnectionTime + TimeOutTime / 6 <= Time.Current && client.ConncetionTryCount == 0)
|
||||
{
|
||||
client.StartedTestConnectionTime = Time.Current;
|
||||
TestConnection(client);
|
||||
}
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 3 <= Time.Current && client.ConncetionTryCount == 1)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.LastConnectionTime + TimeOutTime / 2 <= Time.Current && client.ConncetionTryCount == 2)
|
||||
TestConnection(client);
|
||||
|
||||
if (client.StartedTestConnectionTime + TimeOutTime <= Time.Current)
|
||||
{
|
||||
ConncetedClients.Remove(client);
|
||||
InGameClients.Remove(client);
|
||||
LoadedClients.Remove(client);
|
||||
InGameClients.Remove(client);
|
||||
Logger.Log("Connection to a connected client lost! - " + client.IP + ":" + client.Port, LoggingTarget.Network, LogLevel.Error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Poke!
|
||||
/// </summary>
|
||||
/// <param name="clientInfo"></param>
|
||||
protected void TestConnection(ClientInfo clientInfo)
|
||||
{
|
||||
clientInfo.ConncetionTryCount++;
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(new BasicPacket(ClientInfo) { Test = true });
|
||||
Logger.Log("Testing a client's connection - " + clientInfo.IP + ":" + clientInfo.Port, LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
public void RequestPlayerList()
|
||||
{
|
||||
BasicPacket packet = new BasicPacket(ClientInfo) { RequestPlayerList = true };
|
||||
SendToHost(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell peers to start loading game
|
||||
/// </summary>
|
||||
public virtual void StartLoadingGame()
|
||||
{
|
||||
if (SendClient == null)
|
||||
{
|
||||
BasicPacket packet = new BasicPacket(ClientInfo) { LoadGame = true };
|
||||
|
||||
foreach (ClientInfo client in InMatchClients)
|
||||
packet.PlayerList.Add(client);
|
||||
packet.PlayerList.Add(ClientInfo);
|
||||
|
||||
SendToInMatchClients(packet);
|
||||
|
||||
OnLoadGame?.Invoke(packet.PlayerList);
|
||||
}
|
||||
else
|
||||
Logger.Log("Called StartLoadingGame - We are not the Host!", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this when the game is Loaded and ready to be started
|
||||
/// </summary>
|
||||
public virtual void GameLoaded()
|
||||
{
|
||||
Loaded = true;
|
||||
SendToHost(new BasicPacket(ClientInfo) { Loaded = true });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the Host
|
||||
/// </summary>
|
||||
public virtual void ConnectToHost()
|
||||
{
|
||||
SendToHost(new BasicPacket(ClientInfo) { Connect = true });
|
||||
Logger.Log("Attempting conection to Host. . .", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tell peers to start and starts ours
|
||||
/// </summary>
|
||||
public virtual void SendStartGame()
|
||||
{
|
||||
if (SendClient == null)
|
||||
{
|
||||
SendToLoadedClients(new BasicPacket(ClientInfo) { StartGame = true });
|
||||
InGame = true;
|
||||
Logger.Log("Sending Start Game", LoggingTarget.Network, LogLevel.Verbose);
|
||||
}
|
||||
StartGame?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to the Host
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToHost(Packet packet)
|
||||
{
|
||||
if (SendClient != null)
|
||||
SendClient.SendPacket(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all Connecting clients
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToConnectingClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in ConnectingClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients Connected and waiting
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToConnectedClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients In this Match
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToInMatchClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in InMatchClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients Loaded
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToLoadedClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in LoadedClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to all clients InGame
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToInGameClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in InGameClients)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a Packet to ALL clients we know
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
public void SendToAllClients(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
{
|
||||
SendToConnectingClients(packet);
|
||||
SendToConnectedClients(packet);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send to all but the one that sent it
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
/// <param name="playerID"></param>
|
||||
public void ShareWithOtherPeers(Packet packet)
|
||||
{
|
||||
if (SendClient == null)
|
||||
foreach (ClientInfo clientInfo in ConncetedClients)
|
||||
if (packet.ClientInfo.IP != clientInfo.IP)
|
||||
{
|
||||
NetworkingClient client = new NetworkingClient(true, clientInfo.IP, clientInfo.Port);
|
||||
client.SendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AbortGame()
|
||||
{
|
||||
SendToLoadedClients(new BasicPacket(ClientInfo) { Abort = true });
|
||||
SendToInGameClients(new BasicPacket(ClientInfo) { Abort = true });
|
||||
|
||||
restart:
|
||||
foreach (ClientInfo client in LoadedClients)
|
||||
{
|
||||
LoadedClients.Remove(client);
|
||||
InMatchClients.Add(client);
|
||||
goto restart;
|
||||
}
|
||||
foreach (ClientInfo client in InGameClients)
|
||||
{
|
||||
InGameClients.Remove(client);
|
||||
InMatchClients.Add(client);
|
||||
goto restart;
|
||||
}
|
||||
|
||||
InGame = false;
|
||||
Loaded = false;
|
||||
|
||||
OnAbort?.Invoke();
|
||||
}
|
||||
|
||||
public virtual void Disconnect()
|
||||
{
|
||||
Packet packet = new BasicPacket(ClientInfo) { Disconnect = true };
|
||||
|
||||
OnAbort?.Invoke();
|
||||
InMatch = false;
|
||||
InGame = false;
|
||||
Loaded = false;
|
||||
|
||||
if (SendClient == null)
|
||||
{
|
||||
SendToConnectingClients(packet);
|
||||
SendToConnectedClients(packet);
|
||||
}
|
||||
else
|
||||
SendToHost(packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Die
|
||||
/// </summary>
|
||||
/// <param name="isDisposing"></param>
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
ReceiveClient?.Clear();
|
||||
|
||||
if (SendClient != null)
|
||||
{
|
||||
SendToHost(new BasicPacket(ClientInfo) { Disconnect = true });
|
||||
SendClient.Clear();
|
||||
}
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ClientType
|
||||
{
|
||||
Host,
|
||||
Peer,
|
||||
Server
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Symcol.Core.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class Packet
|
||||
{
|
||||
/// <summary>
|
||||
/// Just a Signature
|
||||
/// </summary>
|
||||
public readonly ClientInfo ClientInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Specify starting size of packet for efficiency
|
||||
/// </summary>
|
||||
public virtual int PacketSize => 1024;
|
||||
|
||||
public Packet(ClientInfo clientInfo)
|
||||
{
|
||||
ClientInfo = clientInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("symcol.Toys")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("symcol.Toys")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("f34ac16c-e590-4d70-a069-a748326852bf")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F34AC16C-E590-4D70-A069-A748326852BF}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Symcol.Core</RootNamespace>
|
||||
<AssemblyName>Symcol.Core</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Cyotek.Drawing.BitmapFont, Version=1.0.0.0, Culture=neutral, PublicKeyToken=58daa28b0b2de221, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Cyotek.Drawing.BitmapFont.1.3.4-beta1\lib\net46\Cyotek.Drawing.BitmapFont.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ManagedBass, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ManagedBass.2.0.3\lib\net45\ManagedBass.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Generic, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net40\SQLite.Net.Platform.Generic.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLite.Net.Platform.Win32, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLite.Net-PCL.3.1.1\lib\net4\SQLite.Net.Platform.Win32.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SQLiteNetExtensions, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GameObjects\SymcolHitbox.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolClickableContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolDialContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolDragContainer.cs" />
|
||||
<Compile Include="Graphics\Containers\SymcolContainer.cs" />
|
||||
<Compile Include="Graphics\UserInterface\SpriteButton.cs" />
|
||||
<Compile Include="Graphics\UserInterface\SymcolWindow.cs" />
|
||||
<Compile Include="Networking\BasicPacket.cs" />
|
||||
<Compile Include="Networking\ClientInfo.cs" />
|
||||
<Compile Include="Networking\NetworkingClient.cs" />
|
||||
<Compile Include="Networking\Packet.cs" />
|
||||
<Compile Include="Networking\NetworkingClientHandler.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-Framework\osu.Framework\osu.Framework.csproj">
|
||||
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
|
||||
<Name>osu.Framework</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,43 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Containers
|
||||
{
|
||||
public class LinkText : OsuSpriteText, IHasTooltip
|
||||
{
|
||||
public string TooltipText => Tooltip;
|
||||
|
||||
public virtual string Tooltip => "";
|
||||
|
||||
private readonly OsuHoverContainer content;
|
||||
|
||||
public override bool HandleKeyboardInput => content.Action != null;
|
||||
public override bool HandleMouseInput => content.Action != null;
|
||||
|
||||
protected override Container<Drawable> Content => content ?? (Container<Drawable>)this;
|
||||
|
||||
public override IEnumerable<Drawable> FlowingChildren => Children;
|
||||
|
||||
public string Url
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
content.Action = () => Process.Start(value);
|
||||
}
|
||||
}
|
||||
|
||||
public LinkText()
|
||||
{
|
||||
AddInternal(content = new OsuHoverContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// TODO: make this more generic
|
||||
/// </summary>
|
||||
public class ProfileLink : LinkText
|
||||
{
|
||||
public override string Tooltip => "View profile in browser";
|
||||
|
||||
public ProfileLink(User user, bool maintainer = false)
|
||||
{
|
||||
if (!maintainer)
|
||||
Text = "Ruleset Creator: " + user.Username;
|
||||
else
|
||||
Text = "Ruleset Maintainer: " + user.Username;
|
||||
|
||||
Url = $@"https://osu.ppy.sh/users/{user.Id}";
|
||||
Font = @"Exo2.0-RegularItalic";
|
||||
TextSize = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.HitObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Mostly stuff copied from Container
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject"></typeparam>
|
||||
public abstract class DrawableSymcolHitObject<TObject> : DrawableHitObject<TObject>
|
||||
where TObject : HitObject
|
||||
{
|
||||
//Future prep?
|
||||
//public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
|
||||
//public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
|
||||
|
||||
protected virtual Container<Drawable> Content => new Container();
|
||||
|
||||
public IReadOnlyList<Drawable> Children
|
||||
{
|
||||
get
|
||||
{
|
||||
return InternalChildren;
|
||||
}
|
||||
set
|
||||
{
|
||||
ChildrenEnumerable = value;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Drawable> ChildrenEnumerable
|
||||
{
|
||||
set
|
||||
{
|
||||
Clear();
|
||||
AddRange(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<Drawable> range)
|
||||
{
|
||||
foreach (Drawable d in range)
|
||||
Add(d);
|
||||
}
|
||||
|
||||
public Drawable Child
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Children.Count != 1)
|
||||
throw new InvalidOperationException($"{nameof(Child)} is only available when there's only 1 in {nameof(Children)}!");
|
||||
|
||||
return Children[0];
|
||||
}
|
||||
set
|
||||
{
|
||||
Clear();
|
||||
Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() => Clear(true);
|
||||
|
||||
public virtual void Clear(bool disposeChildren)
|
||||
{
|
||||
if (Content != null)
|
||||
Content.Clear(disposeChildren);
|
||||
else
|
||||
ClearInternal(disposeChildren);
|
||||
}
|
||||
|
||||
protected DrawableSymcolHitObject(TObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
|
||||
public void Add(Drawable drawable)
|
||||
{
|
||||
AddInternal(drawable);
|
||||
}
|
||||
|
||||
public void Remove(Drawable drawable)
|
||||
{
|
||||
RemoveInternal(drawable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class ChatPacket : Packet
|
||||
{
|
||||
public override int PacketSize => 4096;
|
||||
|
||||
public string Author;
|
||||
|
||||
public string AuthorColor;
|
||||
|
||||
public string Message;
|
||||
|
||||
public ChatPacket(ClientInfo clientInfo) : base(clientInfo)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// Just a client signature basically
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RulesetClientInfo : ClientInfo
|
||||
{
|
||||
public string Username = "";
|
||||
|
||||
public int UserID = -1;
|
||||
|
||||
public string UserPic;
|
||||
|
||||
public string UserBackground;
|
||||
|
||||
public string UserCountry;
|
||||
|
||||
public string CountryFlagName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
//TODO: This NEEDS its own clock to avoid fuckery later on with DoubleTime and HalfTime
|
||||
public class RulesetNetworkingClientHandler : NetworkingClientHandler, IOnlineComponent
|
||||
{
|
||||
public RulesetClientInfo RulesetClientInfo;
|
||||
|
||||
public Action<WorkingBeatmap> OnMapChange;
|
||||
|
||||
private OsuGame osu;
|
||||
|
||||
public RulesetNetworkingClientHandler(ClientType type, string ip, int port = 25570, string thisLocalIp = "0.0.0.0") : base(type, ip, port, thisLocalIp)
|
||||
{
|
||||
if (RulesetClientInfo == null)
|
||||
{
|
||||
RulesetClientInfo = new RulesetClientInfo
|
||||
{
|
||||
Port = port
|
||||
};
|
||||
|
||||
ClientInfo = RulesetClientInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send Map to Peers
|
||||
/// </summary>
|
||||
/// <param name="map"></param>
|
||||
public void SetMap(WorkingBeatmap map)
|
||||
{
|
||||
RulesetPacket packet;
|
||||
try
|
||||
{
|
||||
packet = new RulesetPacket(RulesetClientInfo)
|
||||
{
|
||||
OnlineBeatmapSetID = (int)map.BeatmapSetInfo.OnlineBeatmapSetID,
|
||||
OnlineBeatmapID = (int)map.BeatmapInfo.OnlineBeatmapID
|
||||
};
|
||||
SendToInMatchClients(packet);
|
||||
OnMapChange?.Invoke(osu.Beatmap.Value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
packet = new RulesetPacket(RulesetClientInfo);
|
||||
SendToInMatchClients(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(APIAccess api, OsuGame osu)
|
||||
{
|
||||
api.Register(this);
|
||||
this.osu = osu;
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
RulesetClientInfo.Username = "";
|
||||
RulesetClientInfo.UserID = -1;
|
||||
break;
|
||||
case APIState.Online:
|
||||
RulesetClientInfo.Username = api.LocalUser.Value.Username;
|
||||
RulesetClientInfo.UserID = (int)api.LocalUser.Value.Id;
|
||||
RulesetClientInfo.UserCountry = api.LocalUser.Value.Country.FullName;
|
||||
RulesetClientInfo.CountryFlagName = api.LocalUser.Value.Country.FlagName;
|
||||
RulesetClientInfo.UserPic = api.LocalUser.Value.AvatarUrl;
|
||||
RulesetClientInfo.UserBackground = api.LocalUser.Value.CoverUrl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class RulesetPacket : Packet
|
||||
{
|
||||
public new readonly RulesetClientInfo ClientInfo;
|
||||
|
||||
public override int PacketSize => 4096;
|
||||
|
||||
public int OnlineBeatmapSetID = -1;
|
||||
|
||||
public int OnlineBeatmapID = -1;
|
||||
|
||||
public bool HaveMap;
|
||||
|
||||
public string ChatContent;
|
||||
|
||||
//public string RulesetName = "";
|
||||
|
||||
public RulesetPacket(RulesetClientInfo rulesetClientInfo) : base(rulesetClientInfo)
|
||||
{
|
||||
ClientInfo = rulesetClientInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Symcol.Core.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Networking
|
||||
{
|
||||
[Serializable]
|
||||
public class ScorePacket : Packet
|
||||
{
|
||||
public override int PacketSize => 2048;
|
||||
|
||||
public int Score;
|
||||
|
||||
public ScorePacket(ClientInfo clientInfo, int score) : base(clientInfo)
|
||||
{
|
||||
Score = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Options
|
||||
{
|
||||
public class MultiplayerDropdownEnumOption<T> : MultiplayerOption
|
||||
where T : struct
|
||||
{
|
||||
public readonly Bindable<T> BindableEnum;
|
||||
|
||||
public MultiplayerDropdownEnumOption(Bindable<T> bindable, string name, int quadrant, bool sync = true) : base(name, quadrant, sync)
|
||||
{
|
||||
BindableEnum = bindable;
|
||||
|
||||
OptionContainer.Child = new BetterSettingsEnumDropdown<T>
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Bindable = bindable,
|
||||
};
|
||||
}
|
||||
|
||||
private class BetterSettingsEnumDropdown<T> : SettingsEnumDropdown<T>
|
||||
{
|
||||
protected override Drawable CreateControl() => new BetterOsuEnumDropdown<T>
|
||||
{
|
||||
Margin = new MarginPadding { Top = 5 },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
};
|
||||
|
||||
private class BetterOsuEnumDropdown<T> : OsuEnumDropdown<T>
|
||||
{
|
||||
public BetterOsuEnumDropdown()
|
||||
{
|
||||
Menu.MaxHeight = 160;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using System;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Options
|
||||
{
|
||||
public abstract class MultiplayerOption : Container
|
||||
{
|
||||
protected readonly SpriteText Title;
|
||||
|
||||
protected readonly Container OptionContainer;
|
||||
|
||||
public MultiplayerOption(string name, int quadrant, bool sync = true)
|
||||
{
|
||||
if (quadrant == 1 | quadrant == 3 | quadrant == 5 | quadrant == 7)
|
||||
{
|
||||
switch (quadrant)
|
||||
{
|
||||
case 1:
|
||||
quadrant = 0;
|
||||
break;
|
||||
case 3:
|
||||
quadrant = 1;
|
||||
break;
|
||||
case 5:
|
||||
quadrant = 2;
|
||||
break;
|
||||
case 7:
|
||||
quadrant = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
Position = new Vector2(16, 4 + (64 * quadrant));
|
||||
}
|
||||
else if (quadrant == 2 | quadrant == 4 | quadrant == 6 | quadrant == 8)
|
||||
{
|
||||
switch (quadrant)
|
||||
{
|
||||
case 2:
|
||||
quadrant = 0;
|
||||
break;
|
||||
case 4:
|
||||
quadrant = 1;
|
||||
break;
|
||||
case 6:
|
||||
quadrant = 2;
|
||||
break;
|
||||
case 8:
|
||||
quadrant = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopLeft;
|
||||
Position = new Vector2(22, 4 + (64 * quadrant));
|
||||
}
|
||||
else
|
||||
throw new Exception("Globglogabgalab");
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Width = 0.49f;
|
||||
Height = 80;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Title = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
TextSize = 20,
|
||||
Text = name
|
||||
},
|
||||
OptionContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(-16, 18),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Options
|
||||
{
|
||||
public class MultiplayerToggleOption : MultiplayerOption
|
||||
{
|
||||
public readonly Bindable<bool> BindableBool;
|
||||
|
||||
public MultiplayerToggleOption(Bindable<bool> bindable, string name, int quadrant, bool sync = true) : base(name, quadrant, sync)
|
||||
{
|
||||
BindableBool = bindable;
|
||||
|
||||
Child = new SettingsCheckbox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Bindable = bindable,
|
||||
LabelText = " " + name,
|
||||
Position = new Vector2(-16, 18),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Users;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class Chat : Container, IOnlineComponent
|
||||
{
|
||||
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
|
||||
|
||||
private string playerColorHex = SymcolSettingsSubsection.SymcolConfigManager.GetBindable<string>(SymcolSetting.PlayerColor);
|
||||
|
||||
private User user;
|
||||
|
||||
private readonly FillFlowContainer<ChatMessage> messageContainer;
|
||||
private readonly OsuTextBox textBox;
|
||||
|
||||
public Chat(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
|
||||
rulesetNetworkingClientHandler.OnPacketReceive += (packet) =>
|
||||
{
|
||||
if (packet is ChatPacket chatPacket)
|
||||
Add(chatPacket);
|
||||
if (rulesetNetworkingClientHandler.ClientType == ClientType.Host)
|
||||
rulesetNetworkingClientHandler.ShareWithOtherPeers(packet);
|
||||
};
|
||||
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Height = 0.46f;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.8f
|
||||
},
|
||||
new OsuScrollContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.9f,
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
messageContainer = new FillFlowContainer<ChatMessage>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
},
|
||||
textBox = new OsuTextBox
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.98f,
|
||||
Height = 36,
|
||||
Position = new Vector2(0, -12),
|
||||
Colour = Color4.White,
|
||||
Text = "Type here!"
|
||||
}
|
||||
};
|
||||
|
||||
textBox.OnCommit += (s, r) =>
|
||||
{
|
||||
AddMessage(textBox.Text);
|
||||
textBox.Text = "";
|
||||
};
|
||||
}
|
||||
|
||||
public void Add(ChatPacket packet)
|
||||
{
|
||||
ChatMessage message = new ChatMessage(packet);
|
||||
messageContainer.Add(message);
|
||||
}
|
||||
|
||||
public void AddMessage(string message)
|
||||
{
|
||||
if (message == "" | message == " ")
|
||||
return;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
OsuColour.FromHex(playerColorHex);
|
||||
}
|
||||
catch
|
||||
{
|
||||
playerColorHex = "#ffffff";
|
||||
}
|
||||
|
||||
ChatPacket packet = new ChatPacket(rulesetNetworkingClientHandler.ClientInfo)
|
||||
{
|
||||
Author = user.Username,
|
||||
AuthorColor = playerColorHex,
|
||||
Message = message,
|
||||
};
|
||||
|
||||
rulesetNetworkingClientHandler.SendToHost(packet);
|
||||
rulesetNetworkingClientHandler.SendToInMatchClients(packet);
|
||||
Add(packet);
|
||||
}
|
||||
else
|
||||
Logger.Log("You must be logged in to message!", LoggingTarget.Network, LogLevel.Error);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(APIAccess api)
|
||||
{
|
||||
api.Register(this);
|
||||
}
|
||||
|
||||
public void APIStateChanged(APIAccess api, APIState state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
default:
|
||||
user = null;
|
||||
break;
|
||||
case APIState.Online:
|
||||
user = api.LocalUser.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class ChatMessage : Container
|
||||
{
|
||||
public ChatMessage(ChatPacket packet)
|
||||
{
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = OsuColour.FromHex(packet.AuthorColor),
|
||||
TextSize = 24,
|
||||
Text = packet.Author + ":"
|
||||
},
|
||||
new OsuTextFlowContainer(t => { t.TextSize = 24; })
|
||||
{
|
||||
Position = new OpenTK.Vector2(140, 0),
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = packet.Message
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Users;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MatchPlayer : ClickableContainer, IHasContextMenu
|
||||
{
|
||||
public readonly RulesetClientInfo ClientInfo;
|
||||
|
||||
private readonly Box dim;
|
||||
|
||||
private readonly DrawableFlag countryFlag;
|
||||
private readonly UserCoverBackground profileBackground;
|
||||
private readonly UpdateableAvatar profilePicture;
|
||||
|
||||
public MatchPlayer(RulesetClientInfo clientInfo)
|
||||
{
|
||||
ClientInfo = clientInfo;
|
||||
|
||||
Alpha = 0;
|
||||
Masking = true;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 40f;
|
||||
CornerRadius = 10;
|
||||
|
||||
Country country = new Country
|
||||
{
|
||||
FullName = ClientInfo.UserCountry,
|
||||
FlagName = ClientInfo.CountryFlagName,
|
||||
};
|
||||
|
||||
User user = new User
|
||||
{
|
||||
Username = ClientInfo.Username,
|
||||
Id = ClientInfo.UserID,
|
||||
Country = country,
|
||||
AvatarUrl = ClientInfo.UserPic,
|
||||
CoverUrl = ClientInfo.UserBackground,
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
profileBackground = new UserCoverBackground(user)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
FillMode = FillMode.Fill,
|
||||
OnLoadComplete = d => d.FadeInFromZero(200),
|
||||
},
|
||||
dim = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.8f
|
||||
},
|
||||
profilePicture = new UpdateableAvatar
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = new Vector2(Height * 0.8f),
|
||||
Position = new Vector2(6, 0),
|
||||
User = user,
|
||||
Masking = true,
|
||||
CornerRadius = 6,
|
||||
},
|
||||
countryFlag = new DrawableFlag(country)
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = new Vector2(Height * 0.9f, (Height * 0.9f) * 0.66f),
|
||||
Position = new Vector2(-10, 0)
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Position = new Vector2(Height * 1.1f, 0),
|
||||
TextSize = Height * 0.9f,
|
||||
Text = user.Username
|
||||
}
|
||||
};
|
||||
|
||||
Action = () =>
|
||||
{
|
||||
Process.Start("https://osu.ppy.sh/users/" + user.Id);
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
dim.FadeTo(0.6f, 200);
|
||||
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
base.OnHoverLost(state);
|
||||
|
||||
dim.FadeTo(0.8f, 200);
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem("View Profile", MenuItemType.Standard, () => { }),
|
||||
new OsuMenuItem("Promote to Host", MenuItemType.Highlighted, () => { }),
|
||||
new OsuMenuItem("Kick", MenuItemType.Destructive, () => { }),
|
||||
new OsuMenuItem("Ban", MenuItemType.Destructive, () => { }),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MatchPlayerList : Container
|
||||
{
|
||||
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
|
||||
|
||||
public readonly List<MatchPlayer> MatchPlayers = new List<MatchPlayer>();
|
||||
|
||||
public readonly FillFlowContainer MatchPlayersContianer;
|
||||
|
||||
public MatchPlayerList(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 16;
|
||||
Anchor = Anchor.TopLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Width = 0.49f;
|
||||
Height = 0.45f;
|
||||
Position = new Vector2(10);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.8f)
|
||||
},
|
||||
MatchPlayersContianer = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.98f,
|
||||
Height = 0.96f
|
||||
}
|
||||
};
|
||||
|
||||
rulesetNetworkingClientHandler.OnReceivePlayerList += (players) =>
|
||||
{
|
||||
restart:
|
||||
foreach (MatchPlayer matchPlayer in MatchPlayers)
|
||||
foreach (ClientInfo clientInfo in players)
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
if (rulesetClientInfo.IP + rulesetClientInfo.Port != matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
|
||||
{
|
||||
Add(rulesetClientInfo);
|
||||
players.Remove(clientInfo);
|
||||
goto restart;
|
||||
}
|
||||
};
|
||||
rulesetNetworkingClientHandler.RequestPlayerList();
|
||||
|
||||
rulesetNetworkingClientHandler.OnClientJoin += (clientInfo) =>
|
||||
{
|
||||
foreach (MatchPlayer matchPlayer in MatchPlayers)
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
if (rulesetClientInfo.IP + rulesetClientInfo.Port != matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
|
||||
{
|
||||
Add(rulesetClientInfo);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
rulesetNetworkingClientHandler.OnClientDisconnect += (clientInfo) =>
|
||||
{
|
||||
foreach (MatchPlayer matchPlayer in MatchPlayers)
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
if (rulesetClientInfo.IP + rulesetClientInfo.Port == matchPlayer.ClientInfo.IP + matchPlayer.ClientInfo.Port)
|
||||
{
|
||||
Remove(matchPlayer);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Add(RulesetClientInfo clientInfo)
|
||||
{
|
||||
MatchPlayer matchPlayer = new MatchPlayer(clientInfo);
|
||||
|
||||
Add(matchPlayer);
|
||||
}
|
||||
|
||||
public void Add(MatchPlayer matchPlayer)
|
||||
{
|
||||
MatchPlayers.Add(matchPlayer);
|
||||
MatchPlayersContianer.Add(matchPlayer);
|
||||
matchPlayer.FadeInFromZero(200);
|
||||
}
|
||||
|
||||
public void Remove(MatchPlayer matchPlayer)
|
||||
{
|
||||
MatchPlayers.Remove(matchPlayer);
|
||||
matchPlayer.FadeOutFromOne(200)
|
||||
.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Screens.Symcol.Pieces;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Options;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MatchTools : Container
|
||||
{
|
||||
public readonly Bindable<MatchScreenMode> Mode = new Bindable<MatchScreenMode>() { Default = MatchScreenMode.MapDetails };
|
||||
|
||||
public readonly Bindable<MatchGamemode> GameMode = new Bindable<MatchGamemode>() { Default = MatchGamemode.HeadToHead };
|
||||
|
||||
public readonly OsuTabControl<MatchScreenMode> TabControl;
|
||||
|
||||
public readonly Container SelectedContent;
|
||||
|
||||
public readonly Container MapDetails;
|
||||
|
||||
public Container RulesetSettings;
|
||||
|
||||
public readonly Container SoundBoard;
|
||||
|
||||
private WorkingBeatmap selectedBeatmap;
|
||||
|
||||
private int selectedBeatmapSetID;
|
||||
|
||||
public MatchTools()
|
||||
{
|
||||
Masking = true;
|
||||
CornerRadius = 16;
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Width = 0.49f;
|
||||
Height = 0.45f;
|
||||
Position = new Vector2(-10, 10);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.8f)
|
||||
},
|
||||
TabControl = new OsuTabControl<MatchScreenMode>
|
||||
{
|
||||
Position = new Vector2(72, 0),
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.08f,
|
||||
Width = 0.8f
|
||||
},
|
||||
SelectedContent = new Container
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Height = 0.92f
|
||||
}
|
||||
};
|
||||
TabControl.Current.Value = MatchScreenMode.MapDetails;
|
||||
|
||||
Mode.ValueChanged += (value) =>
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case MatchScreenMode.MapDetails:
|
||||
if (selectedBeatmap != null)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmap);
|
||||
else if (selectedBeatmapSetID != 0)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmapSetID);
|
||||
else
|
||||
SelectedContent.Child = new MapDetailsSection(true);
|
||||
break;
|
||||
case MatchScreenMode.MatchSettings:
|
||||
SelectedContent.Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MultiplayerDropdownEnumOption<MatchGamemode>(GameMode, "Match Gamemode", 1)
|
||||
}
|
||||
};
|
||||
break;
|
||||
case MatchScreenMode.SoundBoard:
|
||||
SelectedContent.Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new HitSoundBoard
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
ButtonSize = 80
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
};
|
||||
Mode.BindTo(TabControl.Current);
|
||||
}
|
||||
|
||||
public void MapChange(WorkingBeatmap workingBeatmap)
|
||||
{
|
||||
if (workingBeatmap == null)
|
||||
{
|
||||
MapChange(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
selectedBeatmap = workingBeatmap;
|
||||
selectedBeatmapSetID = (int)workingBeatmap.BeatmapSetInfo.OnlineBeatmapSetID;
|
||||
|
||||
if (Mode.Value == MatchScreenMode.MapDetails)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmap);
|
||||
}
|
||||
|
||||
public void MapChange(int onlineBeatmapSetID)
|
||||
{
|
||||
selectedBeatmap = null;
|
||||
selectedBeatmapSetID = onlineBeatmapSetID;
|
||||
|
||||
if (Mode.Value == MatchScreenMode.MapDetails)
|
||||
{
|
||||
if (selectedBeatmapSetID != 0 && selectedBeatmapSetID != -1)
|
||||
SelectedContent.Child = new MapDetailsSection(selectedBeatmapSetID);
|
||||
else
|
||||
SelectedContent.Child = new MapDetailsSection(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MapDetailsSection : ClickableContainer
|
||||
{
|
||||
private Sprite beatmapBG;
|
||||
private SpriteText name;
|
||||
private SpriteText artist;
|
||||
private SpriteText difficulty;
|
||||
private SpriteText time;
|
||||
|
||||
private Box dim;
|
||||
|
||||
public MapDetailsSection(WorkingBeatmap workingBeatmap)
|
||||
{
|
||||
draw();
|
||||
|
||||
HitObject lastObject = workingBeatmap.Beatmap.HitObjects.LastOrDefault();
|
||||
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
|
||||
|
||||
beatmapBG.Texture = workingBeatmap.Background;
|
||||
name.Text = workingBeatmap.BeatmapSetInfo.Metadata.Title;
|
||||
artist.Text = "By: " + workingBeatmap.BeatmapSetInfo.Metadata.Artist;
|
||||
difficulty.Text = workingBeatmap.BeatmapInfo.Version + " (" + Math.Round(workingBeatmap.BeatmapInfo.StarDifficulty, 2) + " stars) mapped by " + workingBeatmap.BeatmapInfo.Metadata.AuthorString;
|
||||
time.Text = getBPMRange(workingBeatmap.Beatmap) + " bpm for " + TimeSpan.FromMilliseconds(endTime - workingBeatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss");
|
||||
|
||||
BorderColour = getColour(workingBeatmap.BeatmapInfo);
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Radius = 16,
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = getColour(workingBeatmap.BeatmapInfo).Opacity(0.2f)
|
||||
};
|
||||
Action = () => Process.Start("https://osu.ppy.sh/beatmapsets/" + workingBeatmap.BeatmapSetInfo.OnlineBeatmapSetID);
|
||||
}
|
||||
|
||||
public MapDetailsSection(int onlineBeatmapSetID)
|
||||
{
|
||||
draw();
|
||||
name.Text = "Missing Map!";
|
||||
artist.Text = "Click to open in Browser";
|
||||
Action = () => Process.Start("https://osu.ppy.sh/beatmapsets/" + onlineBeatmapSetID);
|
||||
}
|
||||
|
||||
public MapDetailsSection(bool invalid)
|
||||
{
|
||||
draw();
|
||||
name.Text = "Invalid / No Map Selected!";
|
||||
artist.Text = "Don't hit start, weird things might happen";
|
||||
Action = () => Process.Start("https://osu.ppy.sh/home");
|
||||
}
|
||||
|
||||
private void draw()
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Width = 0.95f;
|
||||
Height = 0.9f;
|
||||
|
||||
Masking = true;
|
||||
BorderColour = Color4.LightBlue;
|
||||
BorderThickness = 4;
|
||||
CornerRadius = 10;
|
||||
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Radius = 16,
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.LightBlue.Opacity(0.2f)
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
beatmapBG = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
},
|
||||
dim = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f
|
||||
},
|
||||
name = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 0),
|
||||
Font = @"Exo2.0-SemiBoldItalic",
|
||||
TextSize = 40
|
||||
},
|
||||
artist = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 38),
|
||||
Font = @"Exo2.0-MediumItalic",
|
||||
TextSize = 24
|
||||
},
|
||||
difficulty = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 64),
|
||||
Font = "Exo2.0-Bold",
|
||||
TextSize = 16
|
||||
},
|
||||
time = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, 84),
|
||||
TextSize = 16
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool OnHover(InputState state)
|
||||
{
|
||||
dim.FadeTo(0.4f, 200);
|
||||
|
||||
return base.OnHover(state);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(InputState state)
|
||||
{
|
||||
base.OnHoverLost(state);
|
||||
|
||||
dim.FadeTo(0.6f, 200);
|
||||
}
|
||||
|
||||
//"Borrowed" stuff
|
||||
private string getBPMRange(Beatmap beatmap)
|
||||
{
|
||||
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
|
||||
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
|
||||
|
||||
if (Precision.AlmostEquals(bpmMin, bpmMax))
|
||||
return $"{bpmMin:0}";
|
||||
|
||||
return $"{bpmMin:0}-{bpmMax:0} (mostly {beatmap.ControlPointInfo.BPMMode:0})";
|
||||
}
|
||||
|
||||
private enum DifficultyRating
|
||||
{
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
Insane,
|
||||
Expert,
|
||||
ExpertPlus
|
||||
}
|
||||
|
||||
private DifficultyRating getDifficultyRating(BeatmapInfo beatmap)
|
||||
{
|
||||
if (beatmap == null)
|
||||
throw new ArgumentNullException(nameof(beatmap));
|
||||
|
||||
var rating = beatmap.StarDifficulty;
|
||||
|
||||
if (rating < 1.5) return DifficultyRating.Easy;
|
||||
if (rating < 2.25) return DifficultyRating.Normal;
|
||||
if (rating < 3.75) return DifficultyRating.Hard;
|
||||
if (rating < 5.25) return DifficultyRating.Insane;
|
||||
if (rating < 6.75) return DifficultyRating.Expert;
|
||||
return DifficultyRating.ExpertPlus;
|
||||
}
|
||||
|
||||
private Color4 getColour(BeatmapInfo beatmap)
|
||||
{
|
||||
OsuColour palette = new OsuColour();
|
||||
switch (getDifficultyRating(beatmap))
|
||||
{
|
||||
case DifficultyRating.Easy:
|
||||
return palette.Green;
|
||||
default:
|
||||
case DifficultyRating.Normal:
|
||||
return palette.Blue;
|
||||
case DifficultyRating.Hard:
|
||||
return palette.Yellow;
|
||||
case DifficultyRating.Insane:
|
||||
return palette.Pink;
|
||||
case DifficultyRating.Expert:
|
||||
return palette.Purple;
|
||||
case DifficultyRating.ExpertPlus:
|
||||
return palette.Gray0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum MatchGamemode
|
||||
{
|
||||
[System.ComponentModel.Description("Head to Head")]
|
||||
HeadToHead,
|
||||
[System.ComponentModel.Description("Head to Head with Live Spectator")]
|
||||
HeadToHeadSpectator,
|
||||
[System.ComponentModel.Description("Team Versus")]
|
||||
TeamVS,
|
||||
[System.ComponentModel.Description("TAG4")]
|
||||
TAG4,
|
||||
[System.ComponentModel.Description("Team TAG4")]
|
||||
TeamTAG4,
|
||||
[System.ComponentModel.Description("Tourny Mode")]
|
||||
Tournement,
|
||||
}
|
||||
|
||||
public enum MatchScreenMode
|
||||
{
|
||||
[System.ComponentModel.Description("Map Details")]
|
||||
MapDetails,
|
||||
[System.ComponentModel.Description("Match Settings")]
|
||||
MatchSettings,
|
||||
[System.ComponentModel.Description("Ruleset Settings")]
|
||||
RulesetSettings,
|
||||
[System.ComponentModel.Description("Sound Board")]
|
||||
SoundBoard
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MultiplayerScoreboard : Container
|
||||
{
|
||||
public readonly Container<MultiplayerScoreboardItem> ScoreboardItems;
|
||||
|
||||
private readonly RulesetNetworkingClientHandler rulesetNetworkingClientHandler;
|
||||
|
||||
private readonly ScoreProcessor scoreProcessor;
|
||||
|
||||
private double updateScoreTime = 0;
|
||||
|
||||
public MultiplayerScoreboard(RulesetNetworkingClientHandler rulesetNetworkingClientHandler, List<ClientInfo> playerList, ScoreProcessor scoreProcessor)
|
||||
{
|
||||
this.rulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
this.scoreProcessor = scoreProcessor;
|
||||
|
||||
AlwaysPresent = true;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Width = 120;
|
||||
|
||||
Position = new Vector2(0, -200);
|
||||
Anchor = Anchor.CentreLeft;
|
||||
Origin = Anchor.TopLeft;
|
||||
|
||||
Child = ScoreboardItems = new Container<MultiplayerScoreboardItem>
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
};
|
||||
|
||||
int i = 1;
|
||||
foreach (ClientInfo clientInfo in playerList)
|
||||
{
|
||||
if (clientInfo is RulesetClientInfo rulesetClientInfo)
|
||||
{
|
||||
ScoreboardItems.Add(new MultiplayerScoreboardItem(rulesetClientInfo, i) { });
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
rulesetNetworkingClientHandler.OnPacketReceive += (Packet packet) =>
|
||||
{
|
||||
if (packet is ScorePacket scorePacket)
|
||||
{
|
||||
rulesetNetworkingClientHandler.ShareWithOtherPeers(scorePacket);
|
||||
foreach (MultiplayerScoreboardItem item in ScoreboardItems)
|
||||
if (scorePacket.ClientInfo == item.ClientInfo)
|
||||
item.Score = scorePacket.Score;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (Time.Current >= updateScoreTime)
|
||||
{
|
||||
updateScoreTime = Time.Current + 500;
|
||||
foreach (MultiplayerScoreboardItem item in ScoreboardItems)
|
||||
if (rulesetNetworkingClientHandler.ClientInfo == item.ClientInfo)
|
||||
item.Score = (int)scoreProcessor.TotalScore.Value;
|
||||
|
||||
rulesetNetworkingClientHandler.SendToHost(new ScorePacket(rulesetNetworkingClientHandler.ClientInfo, (int)scoreProcessor.TotalScore.Value));
|
||||
rulesetNetworkingClientHandler.SendToInGameClients(new ScorePacket(rulesetNetworkingClientHandler.ClientInfo, (int)scoreProcessor.TotalScore.Value));
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (args.Key == Key.Tab)
|
||||
{
|
||||
if (Alpha > 0)
|
||||
this.FadeOut(100);
|
||||
else
|
||||
this.FadeIn(100);
|
||||
}
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Pieces
|
||||
{
|
||||
public class MultiplayerScoreboardItem : Container
|
||||
{
|
||||
public int Score
|
||||
{
|
||||
get { return score; }
|
||||
set
|
||||
{
|
||||
if (value != score)
|
||||
{
|
||||
score = value;
|
||||
scoreText.Text = value.ToString();
|
||||
|
||||
foreach(MultiplayerScoreboardItem item in itemList)
|
||||
if (value > item.Score && Place > item.Place)
|
||||
{
|
||||
Place = item.Place;
|
||||
foreach (MultiplayerScoreboardItem i in itemList)
|
||||
if (i.Place < Place)
|
||||
i.Place -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Place
|
||||
{
|
||||
get { return place; }
|
||||
set
|
||||
{
|
||||
if (Place != place)
|
||||
{
|
||||
place = value;
|
||||
this.MoveTo(new Vector2(0, (-height - 8) * (value - 1)), 200, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int place = 0;
|
||||
|
||||
private int score = 0;
|
||||
|
||||
private const int height = 60;
|
||||
|
||||
public readonly RulesetClientInfo ClientInfo;
|
||||
|
||||
private readonly SpriteText scoreText;
|
||||
|
||||
private static List<MultiplayerScoreboardItem> itemList = new List<MultiplayerScoreboardItem>();
|
||||
|
||||
public MultiplayerScoreboardItem(RulesetClientInfo clientInfo, int place)
|
||||
{
|
||||
ClientInfo = clientInfo;
|
||||
this.place = place;
|
||||
|
||||
itemList.Add(this);
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = height;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 8;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.8f,
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(4),
|
||||
Text = clientInfo.Username
|
||||
},
|
||||
scoreText = new SpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Position = new Vector2(-4),
|
||||
Text = Score.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
this.MoveTo(new Vector2(0, (-height - 8) * (Place - 1)), 200, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
itemList.Remove(this);
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Select;
|
||||
using System;
|
||||
using osu.Game.Screens;
|
||||
using osu.Framework.Screens;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public class MatchSongSelect : SongSelect
|
||||
{
|
||||
public WorkingBeatmap SelectedMap;
|
||||
|
||||
private bool exiting;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => null;
|
||||
|
||||
public Action Action;
|
||||
|
||||
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
public MatchSongSelect(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
base.OnEntering(last);
|
||||
}
|
||||
|
||||
protected override bool OnSelectionFinalised()
|
||||
{
|
||||
if (!exiting)
|
||||
{
|
||||
RulesetNetworkingClientHandler.OnMapChange?.Invoke(null);
|
||||
SelectedMap = Beatmap.Value;
|
||||
Action();
|
||||
exiting = true;
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
Exit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK.Input;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Pieces;
|
||||
using System.Collections.Generic;
|
||||
using Symcol.Core.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public class MultiPlayer : ScreenWithBeatmapBackground, IProvideCursor
|
||||
{
|
||||
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
|
||||
|
||||
protected override float BackgroundParallaxAmount => 0.1f;
|
||||
|
||||
public override bool ShowOverlaysOnEnter => false;
|
||||
|
||||
public Action RestartRequested;
|
||||
|
||||
public override bool AllowBeatmapRulesetChange => false;
|
||||
|
||||
public bool HasFailed { get; private set; }
|
||||
|
||||
public bool AllowPause { get; set; } = false;
|
||||
public bool AllowLeadIn { get; set; } = true;
|
||||
public bool AllowResults { get; set; } = true;
|
||||
|
||||
public int RestartCount;
|
||||
|
||||
public CursorContainer Cursor => RulesetContainer.Cursor;
|
||||
public bool ProvidingUserCursor => RulesetContainer?.Cursor != null && !RulesetContainer.HasReplayLoaded.Value;
|
||||
|
||||
private IAdjustableClock sourceClock;
|
||||
private DecoupleableInterpolatingFramedClock adjustableClock;
|
||||
|
||||
private RulesetInfo ruleset;
|
||||
|
||||
private APIAccess api;
|
||||
|
||||
private ScoreProcessor scoreProcessor;
|
||||
protected RulesetContainer RulesetContainer;
|
||||
|
||||
#region User Settings
|
||||
|
||||
private Bindable<double> dimLevel;
|
||||
private Bindable<double> blurLevel;
|
||||
private Bindable<bool> showStoryboard;
|
||||
private Bindable<bool> mouseWheelDisabled;
|
||||
private Bindable<double> userAudioOffset;
|
||||
|
||||
private SampleChannel sampleRestart;
|
||||
|
||||
#endregion
|
||||
|
||||
private Container storyboardContainer;
|
||||
private DrawableStoryboard storyboard;
|
||||
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
private MultiplayerScoreboard scoreboard;
|
||||
|
||||
private bool loadedSuccessfully => RulesetContainer?.Objects.Any() == true;
|
||||
|
||||
private readonly List<ClientInfo> playerList;
|
||||
|
||||
public MultiPlayer(RulesetNetworkingClientHandler rulesetNetworkingClientHandler, List<ClientInfo> playerList)//, WorkingBeatmap beatmap = null)
|
||||
{
|
||||
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
RulesetNetworkingClientHandler.OnAbort = () => Exit();
|
||||
RulesetNetworkingClientHandler.StartGame = () => start();
|
||||
this.playerList = playerList;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, APIAccess api, OsuConfigManager config)
|
||||
{
|
||||
this.api = api;
|
||||
|
||||
dimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
|
||||
blurLevel = config.GetBindable<double>(OsuSetting.BlurLevel);
|
||||
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
|
||||
sampleRestart = audio.Sample.Get(@"Gameplay/restart");
|
||||
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
|
||||
|
||||
WorkingBeatmap working = Beatmap.Value;
|
||||
Beatmap beatmap;
|
||||
|
||||
try
|
||||
{
|
||||
beatmap = working.Beatmap;
|
||||
|
||||
if (beatmap == null)
|
||||
throw new InvalidOperationException("Beatmap was not loaded");
|
||||
|
||||
ruleset = Ruleset.Value ?? beatmap.BeatmapInfo.Ruleset;
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
try
|
||||
{
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
|
||||
}
|
||||
catch (BeatmapInvalidForRulesetException)
|
||||
{
|
||||
// we may fail to create a RulesetContainer if the beatmap cannot be loaded with the user's preferred ruleset
|
||||
// let's try again forcing the beatmap's ruleset.
|
||||
ruleset = beatmap.BeatmapInfo.Ruleset;
|
||||
rulesetInstance = ruleset.CreateInstance();
|
||||
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true);
|
||||
}
|
||||
|
||||
if (!RulesetContainer.Objects.Any())
|
||||
throw new InvalidOperationException("Beatmap contains no hit objects!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, "Could not load beatmap sucessfully!");
|
||||
|
||||
//couldn't load, hard abort!
|
||||
Exit();
|
||||
return;
|
||||
}
|
||||
|
||||
sourceClock = (IAdjustableClock)working.Track ?? new StopwatchClock();
|
||||
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
|
||||
var firstObjectTime = RulesetContainer.Objects.First().StartTime;
|
||||
adjustableClock.Seek(AllowLeadIn
|
||||
? Math.Min(0, firstObjectTime - Math.Max(beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, beatmap.BeatmapInfo.AudioLeadIn))
|
||||
: firstObjectTime);
|
||||
|
||||
adjustableClock.ProcessFrame();
|
||||
|
||||
// the final usable gameplay clock with user-set offsets applied.
|
||||
var offsetClock = new FramedOffsetClock(adjustableClock);
|
||||
|
||||
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
|
||||
userAudioOffset.TriggerChange();
|
||||
|
||||
scoreProcessor = RulesetContainer.CreateScoreProcessor();
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = offsetClock,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
storyboardContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
},
|
||||
RulesetContainer,
|
||||
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
|
||||
{
|
||||
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
|
||||
ProcessCustomClock = false,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
ProcessCustomClock = false,
|
||||
Breaks = beatmap.Breaks
|
||||
},
|
||||
scoreboard = new MultiplayerScoreboard(RulesetNetworkingClientHandler, playerList, scoreProcessor)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (showStoryboard)
|
||||
initializeStoryboard(false);
|
||||
|
||||
// Bind ScoreProcessor to ourselves
|
||||
scoreProcessor.AllJudged += onCompletion;
|
||||
scoreProcessor.Failed += onFail;
|
||||
|
||||
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToScoreProcessor>())
|
||||
mod.ApplyToScoreProcessor(scoreProcessor);
|
||||
}
|
||||
|
||||
private void applyRateFromMods()
|
||||
{
|
||||
if (sourceClock == null) return;
|
||||
|
||||
sourceClock.Rate = 1;
|
||||
foreach (var mod in Beatmap.Value.Mods.Value.OfType<IApplicableToClock>())
|
||||
mod.ApplyToClock(sourceClock);
|
||||
}
|
||||
|
||||
private void initializeStoryboard(bool asyncLoad)
|
||||
{
|
||||
if (storyboardContainer == null)
|
||||
return;
|
||||
|
||||
var beatmap = Beatmap.Value;
|
||||
|
||||
storyboard = beatmap.Storyboard.CreateDrawable();
|
||||
storyboard.Masking = true;
|
||||
|
||||
if (asyncLoad)
|
||||
LoadComponentAsync(storyboard, storyboardContainer.Add);
|
||||
else
|
||||
storyboardContainer.Add(storyboard);
|
||||
}
|
||||
|
||||
private ScheduledDelegate onCompletionEvent;
|
||||
|
||||
private void onCompletion()
|
||||
{
|
||||
// Only show the completion screen if the player hasn't failed
|
||||
if (scoreProcessor.HasFailed || onCompletionEvent != null)
|
||||
return;
|
||||
|
||||
ValidForResume = false;
|
||||
|
||||
if (!AllowResults) return;
|
||||
|
||||
using (BeginDelayedSequence(1000))
|
||||
{
|
||||
onCompletionEvent = Schedule(delegate
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
var score = new Score
|
||||
{
|
||||
Beatmap = Beatmap.Value.BeatmapInfo,
|
||||
Ruleset = ruleset
|
||||
};
|
||||
scoreProcessor.PopulateScore(score);
|
||||
score.User = RulesetContainer.Replay?.User ?? api.LocalUser.Value;
|
||||
Push(new Results(score));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool onFail()
|
||||
{
|
||||
if (Beatmap.Value.Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
|
||||
return false;
|
||||
|
||||
HasFailed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
|
||||
if (!loadedSuccessfully)
|
||||
return;
|
||||
|
||||
Content.Alpha = 0;
|
||||
Content
|
||||
.ScaleTo(0.7f)
|
||||
.ScaleTo(1, 750, Easing.OutQuint)
|
||||
.Delay(250)
|
||||
.FadeIn(250);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
sourceClock.Reset();
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
adjustableClock.ChangeSource(sourceClock);
|
||||
applyRateFromMods();
|
||||
|
||||
this.Delay(750).Schedule(() =>
|
||||
{
|
||||
Logger.Log("Client finnished loading", LoggingTarget.Network, LogLevel.Verbose);
|
||||
RulesetNetworkingClientHandler.GameLoaded();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void start()
|
||||
{
|
||||
adjustableClock.Start();
|
||||
}
|
||||
|
||||
protected override void OnSuspending(Screen next)
|
||||
{
|
||||
fadeOut();
|
||||
base.OnSuspending(next);
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
fadeOut();
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected override void UpdateBackgroundElements()
|
||||
{
|
||||
if (!IsCurrentScreen) return;
|
||||
|
||||
base.UpdateBackgroundElements();
|
||||
|
||||
if (ShowStoryboard && storyboard == null)
|
||||
initializeStoryboard(true);
|
||||
|
||||
var beatmap = Beatmap.Value;
|
||||
var storyboardVisible = ShowStoryboard && beatmap.Storyboard.HasDrawable;
|
||||
|
||||
storyboardContainer?
|
||||
.FadeColour(OsuColour.Gray(BackgroundOpacity), BACKGROUND_FADE_DURATION, Easing.OutQuint)
|
||||
.FadeTo(storyboardVisible && BackgroundOpacity > 0 ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
|
||||
|
||||
if (storyboardVisible && beatmap.Storyboard.ReplacesBackground)
|
||||
Background?.FadeTo(0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void fadeOut()
|
||||
{
|
||||
const float fade_out_duration = 250;
|
||||
|
||||
RulesetContainer?.FadeOut(fade_out_duration);
|
||||
Content.FadeOut(fade_out_duration);
|
||||
|
||||
hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, Easing.In);
|
||||
|
||||
Background?.FadeTo(1f, fade_out_duration);
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
if (args.Key == Key.Escape)
|
||||
BackOut();
|
||||
|
||||
return base.OnKeyDown(state, args);
|
||||
}
|
||||
|
||||
public void BackOut()
|
||||
{
|
||||
RulesetNetworkingClientHandler.AbortGame();
|
||||
RulesetNetworkingClientHandler.OnAbort();
|
||||
}
|
||||
|
||||
protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Screens;
|
||||
using System;
|
||||
using osu.Framework.Screens;
|
||||
using System.Collections.Generic;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public abstract class RulesetLobbyScreen : OsuScreen
|
||||
{
|
||||
public abstract string RulesetName { get; }
|
||||
|
||||
public abstract RulesetMatchScreen MatchScreen { get; }
|
||||
|
||||
public RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
public readonly SettingsButton HostGameButton;
|
||||
public readonly SettingsButton DirectConnectButton;
|
||||
public readonly SettingsButton JoinGameButton;
|
||||
|
||||
public readonly Container NewGame;
|
||||
protected readonly TextBox HostIP;
|
||||
protected readonly TextBox HostPort;
|
||||
//protected readonly TextBox PublicIp;
|
||||
protected readonly TextBox LocalIp;
|
||||
|
||||
public readonly Container JoinIP;
|
||||
|
||||
public RulesetLobbyScreen()
|
||||
{
|
||||
AlwaysPresent = true;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
HostGameButton = new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Host Game",
|
||||
Action = HostGame
|
||||
},
|
||||
DirectConnectButton = new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Direct Connect",
|
||||
Action = DirectConnect
|
||||
},
|
||||
JoinGameButton = new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Join Game"
|
||||
},
|
||||
NewGame = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = true,
|
||||
Size = new Vector2(400, 300),
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Blue,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
HostIP = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "Host IP Address"
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
HostPort = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "25570"
|
||||
},
|
||||
/*
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 22),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
PublicIp = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 22),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "You're Public IP Address"
|
||||
},
|
||||
*/
|
||||
new Box
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.9f,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 44),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
},
|
||||
LocalIp = new TextBox
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Position = new Vector2(0, 44),
|
||||
Width = 0.48f,
|
||||
Height = 20,
|
||||
Text = "You're Local IP Address"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
MakeCurrent();
|
||||
}
|
||||
|
||||
protected override void OnResuming(Screen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
MakeCurrent();
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
}
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected virtual void HostGame()
|
||||
{
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
}
|
||||
Add(RulesetNetworkingClientHandler = new RulesetNetworkingClientHandler(ClientType.Host, LocalIp.Text, Int32.Parse(HostPort.Text)));
|
||||
|
||||
List<ClientInfo> list = new List<ClientInfo>();
|
||||
list.Add(RulesetNetworkingClientHandler.RulesetClientInfo);
|
||||
|
||||
JoinMatch(list);
|
||||
}
|
||||
|
||||
protected virtual void DirectConnect()
|
||||
{
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
}
|
||||
Add(RulesetNetworkingClientHandler = new RulesetNetworkingClientHandler(ClientType.Peer, HostIP.Text, Int32.Parse(HostPort.Text), LocalIp.Text));
|
||||
|
||||
RulesetNetworkingClientHandler.OnConnectedToHost += (p) => JoinMatch(p);
|
||||
}
|
||||
|
||||
protected virtual void JoinMatch(List<ClientInfo> clientInfos)
|
||||
{
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
MakeCurrent();
|
||||
Push(MatchScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Screens;
|
||||
using Symcol.Core.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Pieces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public abstract class RulesetMatchScreen : OsuScreen
|
||||
{
|
||||
public readonly RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
private readonly MatchPlayerList playerList;
|
||||
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
protected MatchTools MatchTools;
|
||||
|
||||
private readonly Chat chat;
|
||||
|
||||
public RulesetMatchScreen(RulesetNetworkingClientHandler rulesetNetworkingClientHandler)
|
||||
{
|
||||
RulesetNetworkingClientHandler = rulesetNetworkingClientHandler;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.35f,
|
||||
Text = "Leave",
|
||||
Action = () => Exit()
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.3f,
|
||||
Text = "Open Song Select",
|
||||
Action = () => openSongSelect()
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.35f,
|
||||
Text = "Start Match",
|
||||
Action = () => RulesetNetworkingClientHandler.StartLoadingGame()
|
||||
},
|
||||
playerList = new MatchPlayerList(RulesetNetworkingClientHandler),
|
||||
MatchTools = new MatchTools(),
|
||||
chat = new Chat(RulesetNetworkingClientHandler)
|
||||
};
|
||||
|
||||
RulesetNetworkingClientHandler.OnPacketReceive += (Packet packet) =>
|
||||
{
|
||||
if (packet is RulesetPacket rulesetPacket && rulesetPacket.OnlineBeatmapID != -1)
|
||||
foreach (BeatmapSetInfo beatmapSet in beatmaps.GetAllUsableBeatmapSets())
|
||||
if (beatmapSet.OnlineBeatmapSetID == rulesetPacket.OnlineBeatmapSetID)
|
||||
{
|
||||
foreach (BeatmapInfo beatmap in beatmapSet.Beatmaps)
|
||||
if (beatmap.OnlineBeatmapID == rulesetPacket.OnlineBeatmapID)
|
||||
{
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
|
||||
Beatmap.Value.Track.Start();
|
||||
MatchTools.MapChange(Beatmap);
|
||||
RulesetNetworkingClientHandler.OnMapChange?.Invoke(Beatmap);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
MatchTools.MapChange(rulesetPacket.OnlineBeatmapSetID);
|
||||
};
|
||||
|
||||
RulesetNetworkingClientHandler.OnMapChange += (beatmap) => MatchTools.MapChange(beatmap);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
playerList.Add(RulesetNetworkingClientHandler.RulesetClientInfo);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps)
|
||||
{
|
||||
this.beatmaps = beatmaps;
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
MakeCurrent();
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.OnLoadGame = (i) => Load(i);
|
||||
}
|
||||
|
||||
protected override void OnResuming(Screen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
MakeCurrent();
|
||||
if (RulesetNetworkingClientHandler != null)
|
||||
Add(RulesetNetworkingClientHandler);
|
||||
}
|
||||
|
||||
protected override void OnSuspending(Screen next)
|
||||
{
|
||||
base.OnSuspending(next);
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
RulesetNetworkingClientHandler.Disconnect();
|
||||
Remove(RulesetNetworkingClientHandler);
|
||||
RulesetNetworkingClientHandler.Dispose();
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
protected virtual void Load(List<ClientInfo> playerList)
|
||||
{
|
||||
MakeCurrent();
|
||||
Push(new MultiPlayer(RulesetNetworkingClientHandler, playerList));
|
||||
}
|
||||
|
||||
private void openSongSelect()
|
||||
{
|
||||
MatchSongSelect songSelect = new MatchSongSelect(RulesetNetworkingClientHandler);
|
||||
MakeCurrent();
|
||||
Push(songSelect);
|
||||
songSelect.Action = () => RulesetNetworkingClientHandler.SetMap(songSelect.SelectedMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//osu.Game.Screens.Symcol.SymcolMenu
|
||||
//Symcol.Rulesets.Core.SymcolSettingsSubsection
|
||||
#define SymcolMods
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Symcol;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Multiplayer.Screens
|
||||
{
|
||||
public class RulesetMultiplayerSelection : OsuScreen
|
||||
{
|
||||
public static readonly FillFlowContainer<RulesetLobbyItem> LobbyItems = new FillFlowContainer<RulesetLobbyItem>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.85f,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
};
|
||||
|
||||
public RulesetMultiplayerSelection()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Add(LobbyItems);
|
||||
}
|
||||
|
||||
protected override void OnEntering(Screen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
foreach (RulesetLobbyItem item in LobbyItems)
|
||||
item.Action = () => Push(item.RulesetLobbyScreen);
|
||||
}
|
||||
|
||||
protected override bool OnExiting(Screen next)
|
||||
{
|
||||
Remove(LobbyItems);
|
||||
|
||||
#if SymcolMods
|
||||
SymcolSettingsSubsection.RulesetMultiplayerSelection = new RulesetMultiplayerSelection();
|
||||
|
||||
SymcolMenu.RulesetMultiplayerScreen = SymcolSettingsSubsection.RulesetMultiplayerSelection;
|
||||
#endif
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class RulesetLobbyItem : ClickableContainer
|
||||
{
|
||||
public abstract Texture Icon { get; }
|
||||
|
||||
public abstract string RulesetName { get; }
|
||||
|
||||
public virtual Texture Background { get; }
|
||||
|
||||
public abstract RulesetLobbyScreen RulesetLobbyScreen { get; }
|
||||
|
||||
public RulesetLobbyItem()
|
||||
{
|
||||
CornerRadius = 20;
|
||||
Masking = true;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = 100;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = Background
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new Sprite
|
||||
{
|
||||
Size = new Vector2(Height),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Texture = Icon
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Text = RulesetName,
|
||||
TextSize = 60,
|
||||
Position = new Vector2(-20, 0)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Symcol.Rulesets.Core")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Symcol.Rulesets.Core")]
|
||||
[assembly: AssemblyCopyright("Copyright © Shawdooow 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("552b5940-c647-4060-aa4d-61baac415c72")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,97 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Skinning
|
||||
{
|
||||
public abstract class SkinElement : Container
|
||||
{
|
||||
private static string loadedSkin;
|
||||
|
||||
private static ResourceStore<byte[]> skinResources;
|
||||
private static TextureStore skinTextures;
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to get a skin element fron the skin, if no element is found return the default element
|
||||
/// </summary>
|
||||
/// <param name="stockTextures"></param>
|
||||
/// <param name="skin"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="storage"></param>
|
||||
/// <returns></returns>
|
||||
public static Texture GetSkinElement(TextureStore stockTextures, Bindable<string> skin, string fileName, Storage storage)
|
||||
{
|
||||
Texture texture = null;
|
||||
|
||||
string fileNameHd = fileName + "@2x";
|
||||
|
||||
Storage skinStorage = storage.GetStorageForDirectory("Skins\\" + skin);
|
||||
|
||||
if (skin.Value == "default")
|
||||
{
|
||||
texture = stockTextures.Get(fileName + ".png");
|
||||
|
||||
if (texture == null)
|
||||
texture = stockTextures.Get(fileNameHd + ".png");
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
if (loadedSkin != skin.ToString())
|
||||
{
|
||||
loadedSkin = skin.ToString();
|
||||
skinResources = new ResourceStore<byte[]>(new StorageBackedResourceStore(skinStorage));
|
||||
skinTextures = new TextureStore(new RawTextureLoaderStore(skinResources));
|
||||
}
|
||||
|
||||
if (skinStorage.Exists(fileNameHd + ".png"))
|
||||
texture = skinTextures.Get(fileNameHd + ".png");
|
||||
else if (skinStorage.Exists(fileName + ".png"))
|
||||
{
|
||||
texture = skinTextures.Get(fileName + ".png");
|
||||
texture.ScaleAdjust = 1f;
|
||||
}
|
||||
else
|
||||
texture = stockTextures.Get(fileNameHd + ".png");
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will attempt to get a skin element from the skin, if no element is found return null
|
||||
/// </summary>
|
||||
/// <param name="skin"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="storage"></param>
|
||||
/// <returns></returns>
|
||||
public static Texture GetElement(Bindable<string> skin, string fileName, Storage storage)
|
||||
{
|
||||
Texture texture = null;
|
||||
|
||||
string fileNameHd = fileName + "@2x";
|
||||
|
||||
Storage skinStorage = storage.GetStorageForDirectory("Skins\\" + skin);
|
||||
|
||||
if (loadedSkin != skin.ToString())
|
||||
{
|
||||
loadedSkin = skin.ToString();
|
||||
skinResources = new ResourceStore<byte[]>(new StorageBackedResourceStore(skinStorage));
|
||||
skinTextures = new TextureStore(new RawTextureLoaderStore(skinResources));
|
||||
}
|
||||
|
||||
if (skinStorage.Exists(fileNameHd + ".png"))
|
||||
texture = skinTextures.Get(fileNameHd + ".png");
|
||||
else if (skinStorage.Exists(fileName + ".png"))
|
||||
{
|
||||
texture = skinTextures.Get(fileName + ".png");
|
||||
texture.ScaleAdjust = 1f;
|
||||
}
|
||||
else
|
||||
texture = null;
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Skinning
|
||||
{
|
||||
public class SkinConfigReader<T> : IniConfigManager<T>
|
||||
where T : struct
|
||||
{
|
||||
protected override string Filename => @"skin.ini";
|
||||
|
||||
public SkinConfigReader(Storage storage) : base(storage) { }
|
||||
|
||||
protected override bool PerformSave() { return false; }
|
||||
}
|
||||
|
||||
//wildly incomplete
|
||||
public enum ClassicIniParameters
|
||||
{
|
||||
Name,
|
||||
Author,
|
||||
CursorRotate,
|
||||
CursorExpand,
|
||||
CursorCentre
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{552B5940-C647-4060-AA4D-61BAAC415C72}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Symcol.Rulesets.Core</RootNamespace>
|
||||
<AssemblyName>Symcol.Rulesets.Core</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Containers\LinkText.cs" />
|
||||
<Compile Include="Containers\ProfileLink.cs" />
|
||||
<Compile Include="HitObjects\DrawableSymcolHitObject.cs" />
|
||||
<Compile Include="Multiplayer\Networking\ScorePacket.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\Chat.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\ChatMessage.cs" />
|
||||
<Compile Include="Multiplayer\Networking\ChatPacket.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MatchTools.cs" />
|
||||
<Compile Include="Multiplayer\Networking\RulesetClientInfo.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MatchPlayer.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MatchPlayerList.cs" />
|
||||
<Compile Include="Multiplayer\Options\MultiplayerDropdownEnumOption.cs" />
|
||||
<Compile Include="Multiplayer\Options\MultiplayerOption.cs" />
|
||||
<Compile Include="Multiplayer\Options\MultiplayerToggleOption.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MultiplayerScoreboard.cs" />
|
||||
<Compile Include="Multiplayer\Pieces\MultiplayerScoreboardItem.cs" />
|
||||
<Compile Include="Multiplayer\Screens\MatchSongSelect.cs" />
|
||||
<Compile Include="Multiplayer\Screens\MultiPlayer.cs" />
|
||||
<Compile Include="Multiplayer\Screens\RulesetLobbyScreen.cs" />
|
||||
<Compile Include="Multiplayer\Screens\RulesetMatchScreen.cs" />
|
||||
<Compile Include="Multiplayer\Screens\RulesetMultiplayerSelection.cs" />
|
||||
<Compile Include="Multiplayer\Networking\RulesetNetworkingClientHandler.cs" />
|
||||
<Compile Include="Multiplayer\Networking\RulesetPacket.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Skinning\SkinElement.cs" />
|
||||
<Compile Include="Skinning\SkinIniReader.cs" />
|
||||
<Compile Include="SymcolConfigManager.cs" />
|
||||
<Compile Include="SymcolPlayfield.cs" />
|
||||
<Compile Include="SymcolSettingsSubsection.cs" />
|
||||
<Compile Include="SymcolInputManager.cs" />
|
||||
<Compile Include="VectorVideos\VectorVideo.cs" />
|
||||
<Compile Include="Wiki\WikiOptionEnumExplanation.cs" />
|
||||
<Compile Include="Wiki\WikiParagraph.cs" />
|
||||
<Compile Include="Wiki\WikiSubSectionHeader.cs" />
|
||||
<Compile Include="Wiki\WikiOverlay.cs" />
|
||||
<Compile Include="Wiki\WikiHeader.cs" />
|
||||
<Compile Include="Wiki\WikiSection.cs" />
|
||||
<Compile Include="Wiki\WikiSubSectionLinkHeader.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu-Framework\osu.Framework\osu.Framework.csproj">
|
||||
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
|
||||
<Name>osu.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<Project>{d9a367c9-4c1a-489f-9b05-a0cea2b53b58}</Project>
|
||||
<Name>osu.Game.Resources</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
|
||||
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
|
||||
<Name>osu.Game</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Symcol.Core\Symcol.Core.csproj">
|
||||
<Project>{F34AC16C-E590-4D70-A069-A748326852BF}</Project>
|
||||
<Name>Symcol.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,22 @@
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Platform;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public class SymcolConfigManager : IniConfigManager<SymcolSetting>
|
||||
{
|
||||
protected override string Filename => "symcol.ini";
|
||||
|
||||
public SymcolConfigManager(Storage storage) : base(storage) { }
|
||||
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
Set(SymcolSetting.PlayerColor, "#ffffff");
|
||||
}
|
||||
}
|
||||
|
||||
public enum SymcolSetting
|
||||
{
|
||||
PlayerColor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using Symcol.Rulesets.Core.VectorVideos;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public class SymcolInputManager<T> : RulesetInputManager<T>
|
||||
where T : struct
|
||||
{
|
||||
protected virtual bool VectorVideo => false;
|
||||
|
||||
public SymcolInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique)
|
||||
{
|
||||
Child = new VectorVideo();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Networking;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public class SymcolPlayfield : Playfield
|
||||
{
|
||||
public static RulesetNetworkingClientHandler RulesetNetworkingClientHandler;
|
||||
|
||||
public SymcolPlayfield(Vector2 size) : base(size.X)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//osu.Game.Screens.Symcol.SymcolMenu
|
||||
//Symcol.Rulesets.Core.Multiplayer.Screens.RulesetMultiplayerSelection
|
||||
#define SymcolMods
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using Symcol.Rulesets.Core.Wiki;
|
||||
using osu.Game.Screens.Symcol;
|
||||
using Symcol.Rulesets.Core.Multiplayer.Screens;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Logging;
|
||||
|
||||
namespace Symcol.Rulesets.Core
|
||||
{
|
||||
public abstract class SymcolSettingsSubsection : SettingsSubsection
|
||||
{
|
||||
public virtual WikiOverlay Wiki => null;
|
||||
|
||||
public virtual RulesetLobbyItem RulesetLobbyItem => null;
|
||||
|
||||
#if SymcolMods
|
||||
public static RulesetMultiplayerSelection RulesetMultiplayerSelection;
|
||||
#endif
|
||||
|
||||
public static SymcolConfigManager SymcolConfigManager;
|
||||
|
||||
private OsuGame osu;
|
||||
|
||||
public SymcolSettingsSubsection()
|
||||
{
|
||||
|
||||
#if SymcolMods
|
||||
if (RulesetLobbyItem != null)
|
||||
RulesetMultiplayerSelection.LobbyItems.Add(RulesetLobbyItem);
|
||||
|
||||
if (RulesetMultiplayerSelection == null)
|
||||
RulesetMultiplayerSelection = new RulesetMultiplayerSelection();
|
||||
|
||||
SymcolMenu.RulesetMultiplayerScreen = RulesetMultiplayerSelection;
|
||||
#endif
|
||||
#if !SymcolMods
|
||||
Logger.Log("osu.Game mods not installed! Online Multiplayer will not be avalible without them. . .", LoggingTarget.Information, LogLevel.Important);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGame osu, Storage storage)
|
||||
{
|
||||
this.osu = osu;
|
||||
|
||||
if (SymcolConfigManager == null)
|
||||
SymcolConfigManager = new SymcolConfigManager(storage);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (Wiki != null)
|
||||
osu.Add(Wiki);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.VectorVideos
|
||||
{
|
||||
public class VectorVideo : BeatSyncedContainer
|
||||
{
|
||||
public const string FILE_NAME = "VectorVideo.symcol";
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Storage storage)
|
||||
{
|
||||
}
|
||||
|
||||
protected void LoadContent(string args)
|
||||
{
|
||||
string[] parameters = args.Split(',');
|
||||
|
||||
ObjectType objectType = ObjectType.LogoVisualizer;
|
||||
Anchor anchor = Anchor.Centre;
|
||||
Anchor origin = Anchor.Centre;
|
||||
|
||||
bool checkingType = false;
|
||||
|
||||
foreach (string parameter in parameters)
|
||||
{
|
||||
string[] subParameters = parameter.Split('=');
|
||||
|
||||
foreach (string subParameter in subParameters)
|
||||
{
|
||||
if (subParameter == "Type")
|
||||
checkingType = true;
|
||||
|
||||
if (checkingType)
|
||||
switch (subParameter)
|
||||
{
|
||||
case "LogoVisualizer":
|
||||
objectType = ObjectType.LogoVisualizer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLogoVisualizer()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum ObjectType
|
||||
{
|
||||
LogoVisualizer
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using OpenTK.Graphics;
|
||||
using Symcol.Rulesets.Core.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public abstract class WikiHeader : Container
|
||||
{
|
||||
protected abstract Texture RulesetIcon { get; }
|
||||
|
||||
protected abstract string RulesetName { get; }
|
||||
|
||||
protected abstract string RulesetDescription { get; }
|
||||
|
||||
protected virtual string RulesetUrl => $@"https://osu.ppy.sh/home";
|
||||
|
||||
protected virtual User Creator => null;
|
||||
|
||||
protected virtual User Maintainer => null;
|
||||
|
||||
protected virtual string DiscordInvite => $@"https://discord.gg/ppy";
|
||||
|
||||
protected virtual Texture HeaderBackground => null;
|
||||
|
||||
private const float description_height = 150;
|
||||
private const float description_width = 220;
|
||||
private const float icon_size = 200;
|
||||
private const float header_margin = 50;
|
||||
private const float rulesetname_height = 60;
|
||||
|
||||
public WikiHeader()
|
||||
{
|
||||
Masking = true;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = header_margin + icon_size + rulesetname_height;
|
||||
|
||||
|
||||
var user = Creator;
|
||||
bool maintainer = false;
|
||||
string userTitle = "Creator";
|
||||
if (Creator == null)
|
||||
{
|
||||
user = Maintainer;
|
||||
maintainer = true;
|
||||
userTitle = "Maintainer";
|
||||
}
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fill,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = HeaderBackground
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new Sprite
|
||||
{
|
||||
Size = new Vector2(icon_size),
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Texture = RulesetIcon
|
||||
},
|
||||
new LinkText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, icon_size),
|
||||
Text = RulesetName,
|
||||
Url = RulesetUrl,
|
||||
Font = @"Exo2.0-RegularItalic",
|
||||
TextSize = rulesetname_height
|
||||
},
|
||||
new ProfileLink(user, maintainer)
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, icon_size + rulesetname_height),
|
||||
},
|
||||
new LinkText
|
||||
{
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Position = new Vector2(10, icon_size + rulesetname_height + 20),
|
||||
Text = userTitle + "'s Discord server",
|
||||
Url = DiscordInvite,
|
||||
Font = @"Exo2.0-RegularItalic",
|
||||
TextSize = 16
|
||||
},
|
||||
new OsuTextFlowContainer(t => { t.TextSize = 20; })
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = new Vector2(description_width, description_height),
|
||||
Text = RulesetDescription
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiOptionEnumExplanation<T> : Container
|
||||
where T : struct
|
||||
{
|
||||
public OsuTextFlowContainer Description;
|
||||
|
||||
public WikiOptionEnumExplanation(Bindable<T> bindable)
|
||||
{
|
||||
OsuColour osu = new OsuColour();
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = osu.Yellow,
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Size = new Vector2(10, 0.98f),
|
||||
CornerRadius = 5,
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Position = new Vector2(-10, 0),
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.45f,
|
||||
|
||||
Child = new SettingsEnumDropdown<T>
|
||||
{
|
||||
Bindable = bindable
|
||||
}
|
||||
},
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.45f,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
AutoSizeDuration = 100,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
|
||||
Child = Description = new OsuTextFlowContainer(t => { t.TextSize = 20; })
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using System.Linq;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public abstract class WikiOverlay : WaveOverlayContainer
|
||||
{
|
||||
protected abstract WikiHeader Header { get; }
|
||||
protected abstract WikiSection[] Sections { get; }
|
||||
|
||||
private WikiSection lastSection;
|
||||
private SectionsContainer<WikiSection> sectionsContainer;
|
||||
private WikiTabControl tabs;
|
||||
|
||||
public const float CONTENT_X_MARGIN = 100;
|
||||
|
||||
public WikiOverlay()
|
||||
{
|
||||
FirstWaveColour = OsuColour.Gray(0.4f);
|
||||
SecondWaveColour = OsuColour.Gray(0.3f);
|
||||
ThirdWaveColour = OsuColour.Gray(0.2f);
|
||||
FourthWaveColour = OsuColour.Gray(0.1f);
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
RelativePositionAxes = Axes.Both;
|
||||
Width = 0.85f;
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
Masking = true;
|
||||
AlwaysPresent = true;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Colour = Color4.Black.Opacity(0),
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Radius = 10
|
||||
};
|
||||
|
||||
tabs = new WikiTabControl
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Height = 30
|
||||
};
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f)
|
||||
});
|
||||
|
||||
Add(sectionsContainer = new SectionsContainer<WikiSection>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ExpandableHeader = Header,
|
||||
FixedHeader = tabs,
|
||||
HeaderBackground = new Box
|
||||
{
|
||||
Colour = OsuColour.Gray(34),
|
||||
RelativeSizeAxes = Axes.Both
|
||||
}
|
||||
});
|
||||
|
||||
sectionsContainer.SelectedSection.ValueChanged += s =>
|
||||
{
|
||||
if (lastSection != s)
|
||||
{
|
||||
lastSection = s;
|
||||
tabs.Current.Value = lastSection;
|
||||
}
|
||||
};
|
||||
|
||||
tabs.Current.ValueChanged += s =>
|
||||
{
|
||||
if (lastSection == null)
|
||||
{
|
||||
lastSection = sectionsContainer.Children.FirstOrDefault();
|
||||
if (lastSection != null)
|
||||
tabs.Current.Value = lastSection;
|
||||
return;
|
||||
}
|
||||
if (lastSection != s)
|
||||
{
|
||||
lastSection = s;
|
||||
sectionsContainer.ScrollTo(lastSection);
|
||||
}
|
||||
};
|
||||
|
||||
foreach (WikiSection sec in Sections)
|
||||
{
|
||||
if (sec != null)
|
||||
{
|
||||
sectionsContainer.Add(sec);
|
||||
tabs.AddItem(sec);
|
||||
}
|
||||
}
|
||||
|
||||
sectionsContainer.ScrollToTop();
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
FadeEdgeEffectTo(0.5f, APPEAR_DURATION, Easing.In);
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out);
|
||||
}
|
||||
|
||||
private class WikiTabControl : PageTabControl<WikiSection>
|
||||
{
|
||||
public WikiTabControl()
|
||||
{
|
||||
TabContainer.RelativeSizeAxes &= ~Axes.X;
|
||||
TabContainer.AutoSizeAxes |= Axes.X;
|
||||
TabContainer.Anchor |= Anchor.x1;
|
||||
TabContainer.Origin |= Anchor.x1;
|
||||
}
|
||||
|
||||
protected override TabItem<WikiSection> CreateTabItem(WikiSection value) => new WikiTabItem(value);
|
||||
|
||||
protected override Dropdown<WikiSection> CreateDropdown() => null;
|
||||
|
||||
private class WikiTabItem : PageTabItem
|
||||
{
|
||||
public WikiTabItem(WikiSection value) : base(value)
|
||||
{
|
||||
Text.Text = value.Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiParagraph : Container
|
||||
{
|
||||
public WikiParagraph(string text, float textsize = 20)
|
||||
{
|
||||
paragraphNoMarkdown(text, textsize);
|
||||
}
|
||||
|
||||
public WikiParagraph(string text, bool markdown)
|
||||
{
|
||||
if (!markdown)
|
||||
paragraphNoMarkdown(text, 20);
|
||||
else
|
||||
paragraphMarkdown(text, 20);
|
||||
}
|
||||
public WikiParagraph(string text, float textsize, bool markdown)
|
||||
{
|
||||
if (!markdown)
|
||||
paragraphNoMarkdown(text, textsize);
|
||||
else
|
||||
paragraphMarkdown(text, textsize);
|
||||
}
|
||||
|
||||
private void paragraphNoMarkdown(string text, float textsize)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Child = new OsuTextFlowContainer(t => { t.TextSize = textsize; })
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = text
|
||||
};
|
||||
}
|
||||
|
||||
private void paragraphMarkdown(string text, float textsize)
|
||||
{
|
||||
Anchor = Anchor.TopCentre;
|
||||
Origin = Anchor.TopCentre;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Child = new OsuTextFlowContainer(t => { t.TextSize = textsize; })
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Text = text
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public abstract class WikiSection : FillFlowContainer
|
||||
{
|
||||
public abstract string Title { get; }
|
||||
|
||||
private readonly FillFlowContainer content;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
protected WikiSection()
|
||||
{
|
||||
OsuColour osu = new OsuColour();
|
||||
Direction = FillDirection.Vertical;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
RelativeSizeAxes = Axes.X;
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Colour = osu.Yellow,
|
||||
Text = Title,
|
||||
TextSize = 32,
|
||||
Font = @"Exo2.0-Bold",
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Horizontal = WikiOverlay.CONTENT_X_MARGIN,
|
||||
Vertical = 12
|
||||
}
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = WikiOverlay.CONTENT_X_MARGIN,
|
||||
Bottom = 20
|
||||
}
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 1,
|
||||
Colour = OsuColour.Gray(34),
|
||||
EdgeSmoothness = new Vector2(1)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiSubSectionHeader : OsuSpriteText
|
||||
{
|
||||
public WikiSubSectionHeader(string text)
|
||||
{
|
||||
OsuColour osu = new OsuColour();
|
||||
Colour = osu.Pink;
|
||||
Text = text;
|
||||
TextSize = 24;
|
||||
Font = @"Exo2.0-BoldItalic";
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Vertical = 10
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using Symcol.Rulesets.Core.Containers;
|
||||
|
||||
namespace Symcol.Rulesets.Core.Wiki
|
||||
{
|
||||
public class WikiSubSectionLinkHeader : LinkText
|
||||
{
|
||||
public override string Tooltip => tooltip;
|
||||
|
||||
private string tooltip = "";
|
||||
|
||||
public WikiSubSectionLinkHeader(string text, string url, string tooltip = "")
|
||||
{
|
||||
this.tooltip = tooltip;
|
||||
Url = url;
|
||||
OsuColour osu = new OsuColour();
|
||||
Colour = osu.Pink;
|
||||
Text = text;
|
||||
TextSize = 24;
|
||||
Font = @"Exo2.0-BoldItalic";
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Vertical = 10
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
Reference in New Issue
Block a user