Security fix and merge LemonUI
This commit is contained in:
parent
9cf23cac7f
commit
04c9081851
41
RageCoop.Client/LemonUI/CancelEvent.cs
Normal file
41
RageCoop.Client/LemonUI/CancelEvent.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// NO MERGE
|
||||
|
||||
// Taken from the .NET Runtime Repository
|
||||
// https://github.com/dotnet/runtime
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// Under the MIT License
|
||||
#if FIVEM
|
||||
using System;
|
||||
|
||||
namespace LemonUI // Previously System.ComponentModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the method that will handle the event raised when canceling an event.
|
||||
/// </summary>
|
||||
public delegate void CancelEventHandler(object sender, CancelEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// EventArgs used to describe a cancel event.
|
||||
/// </summary>
|
||||
public class CancelEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether we should cancel the operation or not
|
||||
/// </summary>
|
||||
public bool Cancel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public CancelEventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper constructor
|
||||
/// </summary>
|
||||
/// <param name="cancel"></param>
|
||||
public CancelEventArgs(bool cancel) => Cancel = cancel;
|
||||
}
|
||||
}
|
||||
#endif
|
138
RageCoop.Client/LemonUI/Controls.cs
Normal file
138
RageCoop.Client/LemonUI/Controls.cs
Normal file
@ -0,0 +1,138 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage.Native;
|
||||
using Control = Rage.GameControl;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Tools for dealing with controls.
|
||||
/// </summary>
|
||||
internal static class Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets if the player used a controller for the last input.
|
||||
/// </summary>
|
||||
public static bool IsUsingController
|
||||
{
|
||||
get
|
||||
{
|
||||
#if FIVEM
|
||||
return !API.IsInputDisabled(2);
|
||||
#elif RAGEMP
|
||||
return !Invoker.Invoke<bool>(Natives.IsInputDisabled, 2);
|
||||
#elif RPH
|
||||
return !NativeFunction.CallByHash<bool>(0xA571D46727E2B718, 2);
|
||||
#elif SHVDN3
|
||||
return !Function.Call<bool>(Hash._IS_USING_KEYBOARD, 2);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a control was pressed during the last frame.
|
||||
/// </summary>
|
||||
/// <param name="control">The control to check.</param>
|
||||
/// <returns>true if the control was pressed, false otherwise.</returns>
|
||||
public static bool IsJustPressed(Control control)
|
||||
{
|
||||
#if FIVEM
|
||||
return API.IsDisabledControlJustPressed(0, (int)control);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<bool>(Natives.IsDisabledControlJustPressed, 0, (int)control);
|
||||
#elif RPH
|
||||
return NativeFunction.CallByHash<bool>(0x91AEF906BCA88877, 0, (int)control);
|
||||
#elif SHVDN3
|
||||
return Function.Call<bool>(Hash.IS_DISABLED_CONTROL_JUST_PRESSED, 0, (int)control);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a control is currently pressed.
|
||||
/// </summary>
|
||||
/// <param name="control">The control to check.</param>
|
||||
/// <returns>true if the control is pressed, false otherwise.</returns>
|
||||
public static bool IsPressed(Control control)
|
||||
{
|
||||
#if FIVEM
|
||||
return API.IsDisabledControlPressed(0, (int)control);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<bool>(Natives.IsDisabledControlJustPressed, 0, (int)control);
|
||||
#elif RPH
|
||||
return NativeFunction.CallByHash<bool>(0xE2587F8CBBD87B1D, 0, (int)control);
|
||||
#elif SHVDN3
|
||||
return Function.Call<bool>(Hash.IS_DISABLED_CONTROL_PRESSED, 0, (int)control);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables all of the controls during the next frame.
|
||||
/// </summary>
|
||||
public static void DisableAll(int inputGroup = 0)
|
||||
{
|
||||
#if FIVEM
|
||||
API.DisableAllControlActions(inputGroup);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.DisableAllControlActions, inputGroup);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x5F4B6931816E599B, inputGroup);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.DISABLE_ALL_CONTROL_ACTIONS, inputGroup);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables a control during the next frame.
|
||||
/// </summary>
|
||||
/// <param name="control">The control to enable.</param>
|
||||
public static void EnableThisFrame(Control control)
|
||||
{
|
||||
#if FIVEM
|
||||
API.EnableControlAction(0, (int)control, true);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.EnableControlAction, 0, (int)control, true);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x351220255D64C155, 0, (int)control);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.ENABLE_CONTROL_ACTION, 0, (int)control);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Enables a specific set of controls during the next frame.
|
||||
/// </summary>
|
||||
/// <param name="controls">The controls to enable.</param>
|
||||
public static void EnableThisFrame(IEnumerable<Control> controls)
|
||||
{
|
||||
foreach (Control control in controls)
|
||||
{
|
||||
EnableThisFrame(control);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables a control during the next frame.
|
||||
/// </summary>
|
||||
/// <param name="control">The control to disable.</param>
|
||||
public static void DisableThisFrame(Control control)
|
||||
{
|
||||
#if FIVEM
|
||||
API.DisableControlAction(0, (int)control, true);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.DisableControlAction, 0, (int)control, true);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xFE99B66D079CF6BC, 0, (int)control, true);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.DISABLE_CONTROL_ACTION, 0, (int)control, true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
23
RageCoop.Client/LemonUI/Elements/Alignment.cs
Normal file
23
RageCoop.Client/LemonUI/Elements/Alignment.cs
Normal file
@ -0,0 +1,23 @@
|
||||
#if RPH
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// The alignment of the element to draw.
|
||||
/// </summary>
|
||||
public enum Alignment
|
||||
{
|
||||
/// <summary>
|
||||
/// Aligns the element to the Center.
|
||||
/// </summary>
|
||||
Center = 0,
|
||||
/// <summary>
|
||||
/// Aligns the element to the Left.
|
||||
/// </summary>
|
||||
Left = 1,
|
||||
/// <summary>
|
||||
/// Aligns the element to the RIght.
|
||||
/// </summary>
|
||||
Right = 2,
|
||||
}
|
||||
}
|
||||
#endif
|
113
RageCoop.Client/LemonUI/Elements/BaseElement.cs
Normal file
113
RageCoop.Client/LemonUI/Elements/BaseElement.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using LemonUI.Extensions;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for all of the 2D elements.
|
||||
/// </summary>
|
||||
public abstract class BaseElement : I2Dimensional
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// The 1080 scaled position.
|
||||
/// </summary>
|
||||
protected internal PointF literalPosition = PointF.Empty;
|
||||
/// <summary>
|
||||
/// The relative position between 0 and 1.
|
||||
/// </summary>
|
||||
protected internal PointF relativePosition = PointF.Empty;
|
||||
/// <summary>
|
||||
/// The 1080 scaled size.
|
||||
/// </summary>
|
||||
protected internal SizeF literalSize = SizeF.Empty;
|
||||
/// <summary>
|
||||
/// The relative size between 0 and 1.
|
||||
/// </summary>
|
||||
protected internal SizeF relativeSize = SizeF.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The Position of the drawable.
|
||||
/// </summary>
|
||||
public PointF Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return literalPosition;
|
||||
}
|
||||
set
|
||||
{
|
||||
literalPosition = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Size of the drawable.
|
||||
/// </summary>
|
||||
public SizeF Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return literalSize;
|
||||
}
|
||||
set
|
||||
{
|
||||
literalSize = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Color of the drawable.
|
||||
/// </summary>
|
||||
public Color Color { get; set; } = Color.FromArgb(255, 255, 255, 255);
|
||||
/// <summary>
|
||||
/// The rotation of the drawable.
|
||||
/// </summary>
|
||||
public float Heading { get; set; } = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="BaseElement"/> with the specified Position and Size.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the Element.</param>
|
||||
/// <param name="size">The size of the Element.</param>
|
||||
public BaseElement(PointF pos, SizeF size)
|
||||
{
|
||||
literalPosition = pos;
|
||||
literalSize = size;
|
||||
Recalculate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the size and position of this item.
|
||||
/// </summary>
|
||||
public virtual void Recalculate()
|
||||
{
|
||||
relativePosition = literalPosition.ToRelative();
|
||||
relativeSize = literalSize.ToRelative();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Draws the item on the screen.
|
||||
/// </summary>
|
||||
public abstract void Draw();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
33
RageCoop.Client/LemonUI/Elements/Font.cs
Normal file
33
RageCoop.Client/LemonUI/Elements/Font.cs
Normal file
@ -0,0 +1,33 @@
|
||||
// NO MERGE
|
||||
|
||||
#if RPH
|
||||
namespace LemonUI.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum representing the fonts available in game.
|
||||
/// </summary>
|
||||
public enum Font
|
||||
{
|
||||
/// <summary>
|
||||
/// Chalet London Nineteen Sixty.
|
||||
/// </summary>
|
||||
ChaletLondon = 0,
|
||||
/// <summary>
|
||||
/// SignPainter HouseScript Regular.
|
||||
/// </summary>
|
||||
HouseScript = 1,
|
||||
/// <summary>
|
||||
/// Unknown Monospaced Font.
|
||||
/// </summary>
|
||||
Monospace = 2,
|
||||
/// <summary>
|
||||
/// Chalet Comprime Cologne.
|
||||
/// </summary>
|
||||
ChaletComprimeCologne = 4,
|
||||
/// <summary>
|
||||
/// Pricedown.
|
||||
/// </summary>
|
||||
Pricedown = 7
|
||||
}
|
||||
}
|
||||
#endif
|
23
RageCoop.Client/LemonUI/Elements/I2Dimensional.cs
Normal file
23
RageCoop.Client/LemonUI/Elements/I2Dimensional.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// A 2D item that can be drawn on the screen.
|
||||
/// </summary>
|
||||
public interface I2Dimensional : IRecalculable, IDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// The Position of the drawable.
|
||||
/// </summary>
|
||||
PointF Position { get; set; }
|
||||
/// <summary>
|
||||
/// The Size of the drawable.
|
||||
/// </summary>
|
||||
SizeF Size { get; set; }
|
||||
/// <summary>
|
||||
/// The Color of the drawable.
|
||||
/// </summary>
|
||||
Color Color { get; set; }
|
||||
}
|
||||
}
|
68
RageCoop.Client/LemonUI/Elements/IText.cs
Normal file
68
RageCoop.Client/LemonUI/Elements/IText.cs
Normal file
@ -0,0 +1,68 @@
|
||||
#if FIVEM
|
||||
using Alignment = CitizenFX.Core.UI.Alignment;
|
||||
using Font = CitizenFX.Core.UI.Font;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif SHVDN3
|
||||
using Alignment = GTA.UI.Alignment;
|
||||
using Font = GTA.UI.Font;
|
||||
#endif
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// A Drawable screen text.
|
||||
/// </summary>
|
||||
public interface IText : IRecalculable, IDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// The position of the text.
|
||||
/// </summary>
|
||||
PointF Position { get; set; }
|
||||
/// <summary>
|
||||
/// The text to draw.
|
||||
/// </summary>
|
||||
string Text { get; set; }
|
||||
/// <summary>
|
||||
/// The color of the text.
|
||||
/// </summary>
|
||||
Color Color { get; set; }
|
||||
/// <summary>
|
||||
/// The game font to use.
|
||||
/// </summary>
|
||||
Font Font { get; set; }
|
||||
/// <summary>
|
||||
/// The scale of the text.
|
||||
/// </summary>
|
||||
float Scale { get; set; }
|
||||
/// <summary>
|
||||
/// If the text should have a drop down shadow.
|
||||
/// </summary>
|
||||
bool Shadow { get; set; }
|
||||
/// <summary>
|
||||
/// If the text should have an outline.
|
||||
/// </summary>
|
||||
bool Outline { get; set; }
|
||||
/// <summary>
|
||||
/// The alignment of the text.
|
||||
/// </summary>
|
||||
Alignment Alignment { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum distance from X where the text would wrap into a new line.
|
||||
/// </summary>
|
||||
float WordWrap { get; set; }
|
||||
/// <summary>
|
||||
/// The width that the text takes from the screen.
|
||||
/// </summary>
|
||||
float Width { get; }
|
||||
/// <summary>
|
||||
/// The number of lines used by this text.
|
||||
/// </summary>
|
||||
int LineCount { get; }
|
||||
/// <summary>
|
||||
/// The height of each line of text.
|
||||
/// </summary>
|
||||
float LineHeight { get; }
|
||||
}
|
||||
}
|
65
RageCoop.Client/LemonUI/Elements/ScaledRectangle.cs
Normal file
65
RageCoop.Client/LemonUI/Elements/ScaledRectangle.cs
Normal file
@ -0,0 +1,65 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage.Native;
|
||||
#elif SHVDN3
|
||||
using GTA.Native;
|
||||
#endif
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// A 2D rectangle.
|
||||
/// </summary>
|
||||
public class ScaledRectangle : BaseElement
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScaledRectangle"/> with the specified Position and Size.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the Rectangle.</param>
|
||||
/// <param name="size">The size of the Rectangle.</param>
|
||||
public ScaledRectangle(PointF pos, SizeF size) : base(pos, size)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Draws the rectangle on the screen.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
if (Size == SizeF.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#if FIVEM
|
||||
API.DrawRect(relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.DrawRect, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x3A618A217E5154F0, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.DRAW_RECT, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Color.R, Color.G, Color.B, Color.A);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Recalculates the position based on the size.
|
||||
/// </summary>
|
||||
public override void Recalculate()
|
||||
{
|
||||
base.Recalculate();
|
||||
relativePosition.X += relativeSize.Width * 0.5f;
|
||||
relativePosition.Y += relativeSize.Height * 0.5f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
506
RageCoop.Client/LemonUI/Elements/ScaledText.cs
Normal file
506
RageCoop.Client/LemonUI/Elements/ScaledText.cs
Normal file
@ -0,0 +1,506 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core.Native;
|
||||
using CitizenFX.Core.UI;
|
||||
using Font = CitizenFX.Core.UI.Font;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage.Native;
|
||||
#elif SHVDN3
|
||||
using GTA.UI;
|
||||
using GTA.Native;
|
||||
using Font = GTA.UI.Font;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using LemonUI.Extensions;
|
||||
|
||||
namespace LemonUI.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// A text string.
|
||||
/// </summary>
|
||||
public class ScaledText : IText
|
||||
{
|
||||
#region Consistent Values
|
||||
|
||||
/// <summary>
|
||||
/// The size of every chunk of text.
|
||||
/// </summary>
|
||||
private const int chunkSize = 90;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// The absolute 1080p based screen position.
|
||||
/// </summary>
|
||||
private PointF absolutePosition = PointF.Empty;
|
||||
/// <summary>
|
||||
/// The relative 0-1 relative position.
|
||||
/// </summary>
|
||||
private PointF relativePosition = PointF.Empty;
|
||||
/// <summary>
|
||||
/// The raw string of text.
|
||||
/// </summary>
|
||||
private string text = string.Empty;
|
||||
/// <summary>
|
||||
/// The raw string split into equally sized strings.
|
||||
/// </summary>
|
||||
private List<string> chunks = new List<string>();
|
||||
/// <summary>
|
||||
/// The alignment of the item.
|
||||
/// </summary>
|
||||
private Alignment alignment = Alignment.Left;
|
||||
/// <summary>
|
||||
/// The word wrap value passed by the user.
|
||||
/// </summary>
|
||||
private float internalWrap = 0f;
|
||||
/// <summary>
|
||||
/// The real word wrap value based on the position of the text.
|
||||
/// </summary>
|
||||
private float realWrap = 0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The position of the text.
|
||||
/// </summary>
|
||||
public PointF Position
|
||||
{
|
||||
get => absolutePosition;
|
||||
set
|
||||
{
|
||||
absolutePosition = value;
|
||||
relativePosition = value.ToRelative();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The text to draw.
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get => text;
|
||||
set
|
||||
{
|
||||
text = value;
|
||||
Slice();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The color of the text.
|
||||
/// </summary>
|
||||
public Color Color { get; set; } = Color.FromArgb(255, 255, 255, 255);
|
||||
/// <summary>
|
||||
/// The game font to use.
|
||||
/// </summary>
|
||||
public Font Font { get; set; } = Font.ChaletLondon;
|
||||
/// <summary>
|
||||
/// The scale of the text.
|
||||
/// </summary>
|
||||
public float Scale { get; set; } = 1f;
|
||||
/// <summary>
|
||||
/// If the text should have a drop down shadow.
|
||||
/// </summary>
|
||||
public bool Shadow { get; set; } = false;
|
||||
/// <summary>
|
||||
/// If the test should have an outline.
|
||||
/// </summary>
|
||||
public bool Outline { get; set; } = false;
|
||||
/// <summary>
|
||||
/// The alignment of the text.
|
||||
/// </summary>
|
||||
public Alignment Alignment
|
||||
{
|
||||
get => alignment;
|
||||
set
|
||||
{
|
||||
alignment = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The distance from the start position where the text will be wrapped into new lines.
|
||||
/// </summary>
|
||||
public float WordWrap
|
||||
{
|
||||
get
|
||||
{
|
||||
return internalWrap;
|
||||
}
|
||||
set
|
||||
{
|
||||
internalWrap = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The width that the text takes from the screen.
|
||||
/// </summary>
|
||||
public float Width
|
||||
{
|
||||
get
|
||||
{
|
||||
#if FIVEM
|
||||
API.BeginTextCommandWidth("CELL_EMAIL_BCON");
|
||||
Add();
|
||||
return API.EndTextCommandGetWidth(true) * 1f.ToXAbsolute();
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.BeginTextCommandWidth, "CELL_EMAIL_BCON");
|
||||
Add();
|
||||
return Invoker.Invoke<float>(Natives.EndTextCommandGetWidth) * 1f.ToXAbsolute();
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x54CE8AC98E120CAB, "CELL_EMAIL_BCON");
|
||||
Add();
|
||||
return NativeFunction.CallByHash<float>(0x85F061DA64ED2F67, true) * 1f.ToXAbsolute();
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash._BEGIN_TEXT_COMMAND_GET_WIDTH, "CELL_EMAIL_BCON");
|
||||
Add();
|
||||
return Function.Call<float>(Hash._END_TEXT_COMMAND_GET_WIDTH, true) * 1f.ToXAbsolute();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The number of lines used by this text.
|
||||
/// </summary>
|
||||
public int LineCount
|
||||
{
|
||||
get
|
||||
{
|
||||
#if FIVEM
|
||||
API.BeginTextCommandLineCount("CELL_EMAIL_BCON");
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.BeginTextCommandLineCount, "CELL_EMAIL_BCON");
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x521FB041D93DD0E4, "CELL_EMAIL_BCON");
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash._BEGIN_TEXT_COMMAND_LINE_COUNT, "CELL_EMAIL_BCON");
|
||||
#endif
|
||||
Add();
|
||||
#if FIVEM
|
||||
return API.EndTextCommandGetLineCount(relativePosition.X, relativePosition.Y);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<int>(Natives.EndTextCommandGetLineCount, relativePosition.X, relativePosition.Y);
|
||||
#elif RPH
|
||||
return NativeFunction.CallByHash<int>(0x9040DFB09BE75706, relativePosition.X, relativePosition.Y);
|
||||
#elif SHVDN3
|
||||
return Function.Call<int>(Hash._END_TEXT_COMMAND_LINE_COUNT, relativePosition.X, relativePosition.Y);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The relative height of each line in the text.
|
||||
/// </summary>
|
||||
public float LineHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
// Height will always be 1080
|
||||
#if FIVEM
|
||||
return 1080 * API.GetTextScaleHeight(Scale, (int)Font);
|
||||
#elif RAGEMP
|
||||
return 1080 * Invoker.Invoke<float>(Natives.GetTextScaleHeight, Scale, (int)Font);
|
||||
#elif RPH
|
||||
return 1080 * NativeFunction.CallByHash<float>(0xDB88A37483346780, Scale, (int)Font);
|
||||
#elif SHVDN3
|
||||
return 1080 * Function.Call<float>(Hash.GET_RENDERED_CHARACTER_HEIGHT, Scale, (int)Font);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a text with the specified options.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position where the text should be located.</param>
|
||||
/// <param name="text">The text to show.</param>
|
||||
public ScaledText(PointF pos, string text) : this(pos, text, 1f, Font.ChaletLondon)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a text with the specified options.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position where the text should be located.</param>
|
||||
/// <param name="text">The text to show.</param>
|
||||
/// <param name="scale">The scale of the text.</param>
|
||||
public ScaledText(PointF pos, string text, float scale) : this(pos, text, scale, Font.ChaletLondon)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a text with the specified options
|
||||
/// </summary>
|
||||
/// <param name="pos">The position where the text should be located.</param>
|
||||
/// <param name="text">The text to show.</param>
|
||||
/// <param name="scale">The scale of the text.</param>
|
||||
/// <param name="font">The font to use.</param>
|
||||
public ScaledText(PointF pos, string text, float scale, Font font)
|
||||
{
|
||||
Position = pos;
|
||||
Text = text;
|
||||
Scale = scale;
|
||||
Font = font;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
/// <summary>
|
||||
/// Adds the text information for measurement.
|
||||
/// </summary>
|
||||
private void Add()
|
||||
{
|
||||
if (Scale == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#if FIVEM
|
||||
foreach (string chunk in chunks)
|
||||
{
|
||||
API.AddTextComponentString(chunk);
|
||||
}
|
||||
API.SetTextFont((int)Font);
|
||||
API.SetTextScale(1f, Scale);
|
||||
API.SetTextColour(Color.R, Color.G, Color.B, Color.A);
|
||||
API.SetTextJustification((int)Alignment);
|
||||
if (Shadow)
|
||||
{
|
||||
API.SetTextDropShadow();
|
||||
}
|
||||
if (Outline)
|
||||
{
|
||||
API.SetTextOutline();
|
||||
}
|
||||
if (WordWrap > 0)
|
||||
{
|
||||
switch (Alignment)
|
||||
{
|
||||
case Alignment.Center:
|
||||
API.SetTextWrap(relativePosition.X - (realWrap * 0.5f), relativePosition.X + (realWrap * 0.5f));
|
||||
break;
|
||||
case Alignment.Left:
|
||||
API.SetTextWrap(relativePosition.X, relativePosition.X + realWrap);
|
||||
break;
|
||||
case Alignment.Right:
|
||||
API.SetTextWrap(relativePosition.X - realWrap, relativePosition.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Alignment == Alignment.Right)
|
||||
{
|
||||
API.SetTextWrap(0f, relativePosition.X);
|
||||
}
|
||||
#elif RAGEMP
|
||||
foreach (string chunk in chunks)
|
||||
{
|
||||
Invoker.Invoke(Natives.AddTextComponentSubstringPlayerName, chunk);
|
||||
}
|
||||
Invoker.Invoke(Natives.SetTextFont, (int)Font);
|
||||
Invoker.Invoke(Natives.SetTextScale, 1f, Scale);
|
||||
Invoker.Invoke(Natives.SetTextColour, Color.R, Color.G, Color.B, Color.A);
|
||||
Invoker.Invoke(Natives.SetTextJustification, (int)Alignment);
|
||||
if (Shadow)
|
||||
{
|
||||
Invoker.Invoke(Natives.SetTextDropShadow);
|
||||
}
|
||||
if (Outline)
|
||||
{
|
||||
Invoker.Invoke(Natives.SetTextOutline);
|
||||
}
|
||||
if (WordWrap > 0)
|
||||
{
|
||||
switch (Alignment)
|
||||
{
|
||||
case Alignment.Center:
|
||||
Invoker.Invoke(Natives.SetTextWrap, relativePosition.X - (realWrap * 0.5f), relativePosition.X + (realWrap * 0.5f));
|
||||
break;
|
||||
case Alignment.Left:
|
||||
Invoker.Invoke(Natives.SetTextWrap, relativePosition.X, relativePosition.X + realWrap);
|
||||
break;
|
||||
case Alignment.Right:
|
||||
Invoker.Invoke(Natives.SetTextWrap, relativePosition.X - realWrap, relativePosition.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Alignment == Alignment.Right)
|
||||
{
|
||||
Invoker.Invoke(0x63145D9C883A1A70, 0f, relativePosition.X);
|
||||
}
|
||||
#elif RPH
|
||||
foreach (string chunk in chunks)
|
||||
{
|
||||
NativeFunction.CallByHash<int>(0x6C188BE134E074AA, chunk);
|
||||
}
|
||||
NativeFunction.CallByHash<int>(0x66E0276CC5F6B9DA, (int)Font);
|
||||
NativeFunction.CallByHash<int>(0x07C837F9A01C34C9, 1f, Scale);
|
||||
NativeFunction.CallByHash<int>(0xBE6B23FFA53FB442, Color.R, Color.G, Color.B, Color.A);
|
||||
NativeFunction.CallByHash<int>(0x4E096588B13FFECA, (int)Alignment);
|
||||
if (Shadow)
|
||||
{
|
||||
NativeFunction.CallByHash<int>(0x1CA3E9EAC9D93E5E);
|
||||
}
|
||||
if (Outline)
|
||||
{
|
||||
NativeFunction.CallByHash<int>(0x2513DFB0FB8400FE);
|
||||
}
|
||||
if (WordWrap > 0)
|
||||
{
|
||||
switch (Alignment)
|
||||
{
|
||||
case Alignment.Center:
|
||||
NativeFunction.CallByHash<int>(0x63145D9C883A1A70, relativePosition.X - (realWrap * 0.5f), relativePosition.X + (realWrap * 0.5f));
|
||||
break;
|
||||
case Alignment.Left:
|
||||
NativeFunction.CallByHash<int>(0x63145D9C883A1A70, relativePosition.X, relativePosition.X + realWrap);
|
||||
break;
|
||||
case Alignment.Right:
|
||||
NativeFunction.CallByHash<int>(0x63145D9C883A1A70, relativePosition.X - realWrap, relativePosition.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Alignment == Alignment.Right)
|
||||
{
|
||||
NativeFunction.CallByHash<int>(0x63145D9C883A1A70, 0f, relativePosition.X);
|
||||
}
|
||||
#elif SHVDN3
|
||||
foreach (string chunk in chunks)
|
||||
{
|
||||
Function.Call((Hash)0x6C188BE134E074AA, chunk); // _ADD_TEXT_COMPONENT_STRING on v2, ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME on v3
|
||||
}
|
||||
Function.Call(Hash.SET_TEXT_FONT, (int)Font);
|
||||
Function.Call(Hash.SET_TEXT_SCALE, 1f, Scale);
|
||||
Function.Call(Hash.SET_TEXT_COLOUR, Color.R, Color.G, Color.B, Color.A);
|
||||
Function.Call(Hash.SET_TEXT_JUSTIFICATION, (int)Alignment);
|
||||
if (Shadow)
|
||||
{
|
||||
Function.Call(Hash.SET_TEXT_DROP_SHADOW);
|
||||
}
|
||||
if (Outline)
|
||||
{
|
||||
Function.Call(Hash.SET_TEXT_OUTLINE);
|
||||
}
|
||||
if (WordWrap > 0)
|
||||
{
|
||||
switch (Alignment)
|
||||
{
|
||||
case Alignment.Center:
|
||||
Function.Call(Hash.SET_TEXT_WRAP, relativePosition.X - (realWrap * 0.5f), relativePosition.X + (realWrap * 0.5f));
|
||||
break;
|
||||
case Alignment.Left:
|
||||
Function.Call(Hash.SET_TEXT_WRAP, relativePosition.X, relativePosition.X + realWrap);
|
||||
break;
|
||||
case Alignment.Right:
|
||||
Function.Call(Hash.SET_TEXT_WRAP, relativePosition.X - realWrap, relativePosition.X);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (Alignment == Alignment.Right)
|
||||
{
|
||||
Function.Call(Hash.SET_TEXT_WRAP, 0f, relativePosition.X);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Slices the string of text into appropiately saved chunks.
|
||||
/// </summary>
|
||||
private void Slice()
|
||||
{
|
||||
// If the entire text is under 90 bytes, save it as is and return
|
||||
if (Encoding.UTF8.GetByteCount(text) <= chunkSize)
|
||||
{
|
||||
chunks.Clear();
|
||||
chunks.Add(text);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a new list of chunks and a temporary string
|
||||
List<string> newChunks = new List<string>();
|
||||
string temp = string.Empty;
|
||||
|
||||
// Iterate over the characters in the string
|
||||
foreach (char character in text)
|
||||
{
|
||||
// Create a temporary string with the character
|
||||
string with = string.Concat(temp, character);
|
||||
// If this string is higher than 90 bytes, add the existing string onto the list
|
||||
if (Encoding.UTF8.GetByteCount(with) > chunkSize)
|
||||
{
|
||||
newChunks.Add(temp);
|
||||
temp = character.ToString();
|
||||
continue;
|
||||
}
|
||||
// And save the new string generated
|
||||
temp = with;
|
||||
}
|
||||
|
||||
// If after finishing we still have a piece, save it
|
||||
if (temp != string.Empty)
|
||||
{
|
||||
newChunks.Add(temp);
|
||||
}
|
||||
|
||||
// Once we have finished, replace the old chunks
|
||||
chunks = newChunks;
|
||||
}
|
||||
/// <summary>
|
||||
/// Recalculates the size, position and word wrap of this item.
|
||||
/// </summary>
|
||||
public void Recalculate()
|
||||
{
|
||||
// Do the normal Size and Position recalculation
|
||||
relativePosition = absolutePosition.ToRelative();
|
||||
// And recalculate the word wrap if necessary
|
||||
if (internalWrap <= 0)
|
||||
{
|
||||
realWrap = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
realWrap = internalWrap.ToXRelative();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Draws the text on the screen.
|
||||
/// </summary>
|
||||
public void Draw()
|
||||
{
|
||||
#if FIVEM
|
||||
API.SetTextEntry("CELL_EMAIL_BCON");
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.BeginTextCommandDisplayText, "CELL_EMAIL_BCON");
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x25FBB336DF1804CB, "CELL_EMAIL_BCON");
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0x25FBB336DF1804CB, "CELL_EMAIL_BCON"); // _SET_TEXT_ENTRY on v2, BEGIN_TEXT_COMMAND_DISPLAY_TEXT on v3
|
||||
#endif
|
||||
|
||||
Add();
|
||||
|
||||
#if FIVEM
|
||||
API.DrawText(relativePosition.X, relativePosition.Y);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.DrawDebugText, relativePosition.X, relativePosition.Y);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xCD015E5BB0D96A57, relativePosition.X, relativePosition.Y);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0xCD015E5BB0D96A57, relativePosition.X, relativePosition.Y); // _DRAW_TEXT on v2, END_TEXT_COMMAND_DISPLAY_TEXT on v3
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
144
RageCoop.Client/LemonUI/Elements/ScaledTexture.cs
Normal file
144
RageCoop.Client/LemonUI/Elements/ScaledTexture.cs
Normal file
@ -0,0 +1,144 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage.Native;
|
||||
#elif SHVDN3
|
||||
using GTA.Native;
|
||||
#endif
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Elements
|
||||
{
|
||||
/// <summary>
|
||||
/// A 2D game texture.
|
||||
/// </summary>
|
||||
public class ScaledTexture : BaseElement
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The dictionary where the texture is loaded.
|
||||
/// </summary>
|
||||
public string Dictionary { get; set; }
|
||||
/// <summary>
|
||||
/// The texture to draw from the dictionary.
|
||||
/// </summary>
|
||||
public string Texture { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScaledTexture"/> with a Position and Size of Zero.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">The dictionary where the texture is located.</param>
|
||||
/// <param name="texture">The texture to draw.</param>
|
||||
public ScaledTexture(string dictionary, string texture) : this(PointF.Empty, SizeF.Empty, dictionary, texture)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ScaledTexture"/> with a Position and Size of zero.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the Texture.</param>
|
||||
/// <param name="size">The size of the Texture.</param>
|
||||
/// <param name="dictionary">The dictionary where the texture is located.</param>
|
||||
/// <param name="texture">The texture to draw.</param>
|
||||
public ScaledTexture(PointF pos, SizeF size, string dictionary, string texture) : base(pos, size)
|
||||
{
|
||||
Dictionary = dictionary;
|
||||
Texture = texture;
|
||||
Request();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Functions
|
||||
|
||||
/// <summary>
|
||||
/// Requests the texture dictionary for this class.
|
||||
/// </summary>
|
||||
private void Request()
|
||||
{
|
||||
#if FIVEM
|
||||
if (!API.HasStreamedTextureDictLoaded(Dictionary))
|
||||
{
|
||||
API.RequestStreamedTextureDict(Dictionary, true);
|
||||
}
|
||||
#elif RAGEMP
|
||||
if (!Invoker.Invoke<bool>(Natives.HasStreamedTextureDictLoaded, Dictionary))
|
||||
{
|
||||
Invoker.Invoke(Natives.RequestStreamedTextureDict, Dictionary, true);
|
||||
}
|
||||
#elif RPH
|
||||
if (!NativeFunction.CallByHash<bool>(0x0145F696AAAAD2E4, Dictionary))
|
||||
{
|
||||
NativeFunction.CallByHash<int>(0xDFA2EF8E04127DD5, Dictionary, true);
|
||||
}
|
||||
#elif SHVDN3
|
||||
if (!Function.Call<bool>(Hash.HAS_STREAMED_TEXTURE_DICT_LOADED, Dictionary))
|
||||
{
|
||||
Function.Call(Hash.REQUEST_STREAMED_TEXTURE_DICT, Dictionary, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Draws the texture on the screen.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
if (Size == SizeF.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Request();
|
||||
#if FIVEM
|
||||
API.DrawSprite(Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.DrawSprite, Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xE7FFAE5EBF23D890, Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.DRAW_SPRITE, Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws a specific part of the texture on the screen.
|
||||
/// </summary>
|
||||
public void DrawSpecific(PointF topLeft, PointF bottomRight)
|
||||
{
|
||||
if (Size == SizeF.Empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Request();
|
||||
#if FIVEM
|
||||
API.DrawSpriteUv(Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0x95812F9B26074726, Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x95812F9B26074726, Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0x95812F9B26074726, Dictionary, Texture, relativePosition.X, relativePosition.Y, relativeSize.Width, relativeSize.Height, topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y, Heading, Color.R, Color.G, Color.B, Color.A);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Recalculates the position based on the size.
|
||||
/// </summary>
|
||||
public override void Recalculate()
|
||||
{
|
||||
base.Recalculate();
|
||||
relativePosition.X += relativeSize.Width * 0.5f;
|
||||
relativePosition.Y += relativeSize.Height * 0.5f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
49
RageCoop.Client/LemonUI/Extensions/Float.cs
Normal file
49
RageCoop.Client/LemonUI/Extensions/Float.cs
Normal file
@ -0,0 +1,49 @@
|
||||
namespace LemonUI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for the float class.
|
||||
/// </summary>
|
||||
public static class FloatExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an absolute X or Width float to a relative one.
|
||||
/// </summary>
|
||||
/// <param name="fin">The float to convert.</param>
|
||||
/// <returns>A relative float between 0 and 1.</returns>
|
||||
public static float ToXRelative(this float fin)
|
||||
{
|
||||
Screen.ToRelative(fin, 0, out float fout, out _);
|
||||
return fout;
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts an absolute Y or Height float to a relative one.
|
||||
/// </summary>
|
||||
/// <param name="fin">The float to convert.</param>
|
||||
/// <returns>A relative float between 0 and 1.</returns>
|
||||
public static float ToYRelative(this float fin)
|
||||
{
|
||||
Screen.ToRelative(0, fin, out _, out float fout);
|
||||
return fout;
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts an relative X or Width float to an absolute one.
|
||||
/// </summary>
|
||||
/// <param name="fin">The float to convert.</param>
|
||||
/// <returns>An absolute float.</returns>
|
||||
public static float ToXAbsolute(this float fin)
|
||||
{
|
||||
Screen.ToAbsolute(fin, 0, out float fout, out _);
|
||||
return fout;
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts an relative Y or Height float to an absolute one.
|
||||
/// </summary>
|
||||
/// <param name="fin">The float to convert.</param>
|
||||
/// <returns>An absolute float.</returns>
|
||||
public static float ToYAbsolute(this float fin)
|
||||
{
|
||||
Screen.ToAbsolute(0, fin, out _, out float fout);
|
||||
return fout;
|
||||
}
|
||||
}
|
||||
}
|
31
RageCoop.Client/LemonUI/Extensions/Point.cs
Normal file
31
RageCoop.Client/LemonUI/Extensions/Point.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for the Point and PointF classes.
|
||||
/// </summary>
|
||||
public static class PointExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an absolute 1080-based position into a relative one.
|
||||
/// </summary>
|
||||
/// <param name="point">The absolute PointF.</param>
|
||||
/// <returns>A new PointF with relative values.</returns>
|
||||
public static PointF ToRelative(this PointF point)
|
||||
{
|
||||
Screen.ToRelative(point.X, point.Y, out float x, out float y);
|
||||
return new PointF(x, y);
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts a normalized 0-1 position into an absolute one.
|
||||
/// </summary>
|
||||
/// <param name="point">The relative PointF.</param>
|
||||
/// <returns>A new PointF with absolute values.</returns>
|
||||
public static PointF ToAbsolute(this PointF point)
|
||||
{
|
||||
Screen.ToAbsolute(point.X, point.Y, out float x, out float y);
|
||||
return new PointF(x, y);
|
||||
}
|
||||
}
|
||||
}
|
31
RageCoop.Client/LemonUI/Extensions/Size.cs
Normal file
31
RageCoop.Client/LemonUI/Extensions/Size.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for the Size and SizeF classes.
|
||||
/// </summary>
|
||||
public static class SizeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an absolute 1080-based size into a relative one.
|
||||
/// </summary>
|
||||
/// <param name="size">The absolute SizeF.</param>
|
||||
/// <returns>A new SizeF with relative values.</returns>
|
||||
public static SizeF ToRelative(this SizeF size)
|
||||
{
|
||||
Screen.ToRelative(size.Width, size.Height, out float width, out float height);
|
||||
return new SizeF(width, height);
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts a normalized 0-1 size into an absolute one.
|
||||
/// </summary>
|
||||
/// <param name="size">The relative SizeF.</param>
|
||||
/// <returns>A new SizeF with absolute values.</returns>
|
||||
public static SizeF ToAbsolute(this SizeF size)
|
||||
{
|
||||
Screen.ToAbsolute(size.Width, size.Height, out float width, out float height);
|
||||
return new SizeF(width, height);
|
||||
}
|
||||
}
|
||||
}
|
36
RageCoop.Client/LemonUI/IContainer.cs
Normal file
36
RageCoop.Client/LemonUI/IContainer.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a container that can hold other UI Elements.
|
||||
/// </summary>
|
||||
public interface IContainer<T> : IRecalculable, IProcessable
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the specified item into the Container.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to add.</param>
|
||||
void Add(T item);
|
||||
/// <summary>
|
||||
/// Removes the item from the container.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to remove.</param>
|
||||
void Remove(T item);
|
||||
/// <summary>
|
||||
/// Removes all of the items that match the function.
|
||||
/// </summary>
|
||||
/// <param name="func">The function to check items.</param>
|
||||
void Remove(Func<T, bool> func);
|
||||
/// <summary>
|
||||
/// Clears all of the items in the container.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
/// <summary>
|
||||
/// Checks if the item is part of the container.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to check.</param>
|
||||
/// <returns><see langword="true"/> if the item is in this container, <see langword="false"/> otherwise.</returns>
|
||||
bool Contains(T item);
|
||||
}
|
||||
}
|
13
RageCoop.Client/LemonUI/IDrawable.cs
Normal file
13
RageCoop.Client/LemonUI/IDrawable.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an item that can be drawn.
|
||||
/// </summary>
|
||||
public interface IDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws the item on the screen.
|
||||
/// </summary>
|
||||
void Draw();
|
||||
}
|
||||
}
|
17
RageCoop.Client/LemonUI/IProcessable.cs
Normal file
17
RageCoop.Client/LemonUI/IProcessable.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for items that can be processed in an Object Pool.
|
||||
/// </summary>
|
||||
public interface IProcessable
|
||||
{
|
||||
/// <summary>
|
||||
/// If this processable item is visible on the screen.
|
||||
/// </summary>
|
||||
bool Visible { get; set; }
|
||||
/// <summary>
|
||||
/// Processes the object.
|
||||
/// </summary>
|
||||
void Process();
|
||||
}
|
||||
}
|
13
RageCoop.Client/LemonUI/IRecalculable.cs
Normal file
13
RageCoop.Client/LemonUI/IRecalculable.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for classes that have values that need to be recalculated on resolution changes.
|
||||
/// </summary>
|
||||
public interface IRecalculable
|
||||
{
|
||||
/// <summary>
|
||||
/// Recalculates the values.
|
||||
/// </summary>
|
||||
void Recalculate();
|
||||
}
|
||||
}
|
67
RageCoop.Client/LemonUI/Menus/BadgeSet.cs
Normal file
67
RageCoop.Client/LemonUI/Menus/BadgeSet.cs
Normal file
@ -0,0 +1,67 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a badge that can be applied to a <see cref="NativeItem"/>.
|
||||
/// </summary>
|
||||
public class BadgeSet
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The texture dictionary where the normal texture is located.
|
||||
/// </summary>
|
||||
public string NormalDictionary { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// The texture to use when the item is not hovered.
|
||||
/// </summary>
|
||||
public string NormalTexture { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// The texture dictionary where the normal texture is located.
|
||||
/// </summary>
|
||||
public string HoveredDictionary { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// The texture to use when the item is hovered.
|
||||
/// </summary>
|
||||
public string HoveredTexture { get; set; } = string.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new empty <see cref="BadgeSet"/>.
|
||||
/// </summary>
|
||||
public BadgeSet()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="BadgeSet"/> where both textures are in the same dictionary.
|
||||
/// </summary>
|
||||
/// <param name="dict">The dictionary where the textures are located.</param>
|
||||
/// <param name="normal">The normal texture name.</param>
|
||||
/// <param name="hovered">The hovered texture name.</param>
|
||||
public BadgeSet(string dict, string normal, string hovered)
|
||||
{
|
||||
NormalDictionary = dict;
|
||||
NormalTexture = normal;
|
||||
HoveredDictionary = dict;
|
||||
HoveredTexture = hovered;
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="BadgeSet"/> where both textures are in different dictionaries.
|
||||
/// </summary>
|
||||
/// <param name="normalDict">The dictionary where the normal texture is located.</param>
|
||||
/// <param name="normalTexture">The normal texture name.</param>
|
||||
/// <param name="hoveredDict">The dictionary where the hovered texture is located.</param>
|
||||
/// <param name="hoveredTexture">The hovered texture name.</param>
|
||||
public BadgeSet(string normalDict, string normalTexture, string hoveredDict, string hoveredTexture)
|
||||
{
|
||||
NormalDictionary = normalDict;
|
||||
NormalTexture = normalTexture;
|
||||
HoveredDictionary = hoveredDict;
|
||||
HoveredTexture = hoveredTexture;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
101
RageCoop.Client/LemonUI/Menus/ColorSet.cs
Normal file
101
RageCoop.Client/LemonUI/Menus/ColorSet.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the different colors required to make the colors of a <see cref="NativeItem"/> dynamic.
|
||||
/// </summary>
|
||||
public class ColorSet
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private static readonly Color colorWhite = Color.FromArgb(255, 255, 255, 255);
|
||||
private static readonly Color colorWhiteSmoke = Color.FromArgb(255, 245, 245, 245);
|
||||
private static readonly Color colorBlack = Color.FromArgb(255, 0, 0, 0);
|
||||
private static readonly Color colorDisabled = Color.FromArgb(255, 163, 159, 148);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.Title"/> when the <see cref="NativeItem"/> is not hovered and enabled.
|
||||
/// </summary>
|
||||
public Color TitleNormal { get; set; } = colorWhiteSmoke;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.Title"/> when the <see cref="NativeItem"/> is hovered.
|
||||
/// </summary>
|
||||
public Color TitleHovered { get; set; } = colorBlack;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.Title"/> when the <see cref="NativeItem"/> is disabled.
|
||||
/// </summary>
|
||||
public Color TitleDisabled { get; set; } = colorDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.AltTitle"/> when the <see cref="NativeItem"/> is not hovered and enabled.
|
||||
/// </summary>
|
||||
public Color AltTitleNormal { get; set; } = colorWhiteSmoke;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.AltTitle"/> when the <see cref="NativeItem"/> is hovered.
|
||||
/// </summary>
|
||||
public Color AltTitleHovered { get; set; } = colorBlack;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.AltTitle"/> when the <see cref="NativeItem"/> is disabled.
|
||||
/// </summary>
|
||||
public Color AltTitleDisabled { get; set; } = colorDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeSlidableItem"/> arrows when the item is not hovered and enabled.
|
||||
/// </summary>
|
||||
public Color ArrowsNormal { get; set; } = colorWhiteSmoke;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeSlidableItem"/> arrows when the item is hovered.
|
||||
/// </summary>
|
||||
public Color ArrowsHovered { get; set; } = colorBlack;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeSlidableItem"/> arrows when the item is disabled.
|
||||
/// </summary>
|
||||
public Color ArrowsDisabled { get; set; } = colorDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.LeftBadge"/> when the <see cref="NativeItem"/> is not hovered and enabled.
|
||||
/// </summary>
|
||||
public Color BadgeLeftNormal { get; set; } = colorWhite;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.LeftBadge"/> when the <see cref="NativeItem"/> is hovered.
|
||||
/// </summary>
|
||||
public Color BadgeLeftHovered { get; set; } = colorWhite;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.LeftBadge"/> when the <see cref="NativeItem"/> is disabled.
|
||||
/// </summary>
|
||||
public Color BadgeLeftDisabled { get; set; } = colorWhite;
|
||||
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.RightBadge"/> or <see cref="NativeCheckboxItem"/> checkbox when the <see cref="NativeItem"/> is not hovered and enabled.
|
||||
/// </summary>
|
||||
public Color BadgeRightNormal { get; set; } = colorWhite;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.RightBadge"/> or <see cref="NativeCheckboxItem"/> checkbox when the <see cref="NativeItem"/> is hovered.
|
||||
/// </summary>
|
||||
public Color BadgeRightHovered { get; set; } = colorWhite;
|
||||
/// <summary>
|
||||
/// The color of the <see cref="NativeItem.RightBadge"/> or <see cref="NativeCheckboxItem"/> checkbox when the <see cref="NativeItem"/> is disabled.
|
||||
/// </summary>
|
||||
public Color BadgeRightDisabled { get; set; } = colorWhite;
|
||||
|
||||
/// <summary>
|
||||
/// The normal color of the custom background if <see cref="NativeItem.UseCustomBackground"/> is set to <see langword="true"/>.
|
||||
/// </summary>
|
||||
public Color BackgroundNormal { get; set; } = colorWhite;
|
||||
/// <summary>
|
||||
/// The hovered color of the custom background if <see cref="NativeItem.UseCustomBackground"/> is set to <see langword="true"/>.
|
||||
/// </summary>
|
||||
public Color BackgroundHovered { get; set; } = colorWhite;
|
||||
/// <summary>
|
||||
/// The disabled color of the custom background if <see cref="NativeItem.UseCustomBackground"/> is set to <see langword="true"/>.
|
||||
/// </summary>
|
||||
public Color BackgroundDisabled { get; set; } = colorWhite;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
22
RageCoop.Client/LemonUI/Menus/ColorTitleStyle.cs
Normal file
22
RageCoop.Client/LemonUI/Menus/ColorTitleStyle.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// The Style of title for the Color Panel.
|
||||
/// </summary>
|
||||
public enum ColorTitleStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Does not shows any Title.
|
||||
/// The count will still be shown if <see cref="NativeColorPanel.ShowCount"/> is set to <see langword="true"/>.
|
||||
/// </summary>
|
||||
None = -1,
|
||||
/// <summary>
|
||||
/// Shows a Simple Title for all of the Colors.
|
||||
/// </summary>
|
||||
Simple = 0,
|
||||
/// <summary>
|
||||
/// Shows the Color Name as the Title.
|
||||
/// </summary>
|
||||
ColorName = 1
|
||||
}
|
||||
}
|
21
RageCoop.Client/LemonUI/Menus/CountVisibility.cs
Normal file
21
RageCoop.Client/LemonUI/Menus/CountVisibility.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// The visibility setting for the Item Count of the Menu.
|
||||
/// </summary>
|
||||
public enum CountVisibility
|
||||
{
|
||||
/// <summary>
|
||||
/// The Item Count is never shown.
|
||||
/// </summary>
|
||||
Never = -1,
|
||||
/// <summary>
|
||||
/// The Item Count is shown when is not possible to show all of the items in the screen.
|
||||
/// </summary>
|
||||
Auto = 0,
|
||||
/// <summary>
|
||||
/// The Item Count is always shown.
|
||||
/// </summary>
|
||||
Always = 1
|
||||
}
|
||||
}
|
21
RageCoop.Client/LemonUI/Menus/Direction.cs
Normal file
21
RageCoop.Client/LemonUI/Menus/Direction.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// The movement direction of the item change.
|
||||
/// </summary>
|
||||
public enum Direction
|
||||
{
|
||||
/// <summary>
|
||||
/// The Direction is Unknown.
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
/// <summary>
|
||||
/// The item was moved to the Left.
|
||||
/// </summary>
|
||||
Left = 1,
|
||||
/// <summary>
|
||||
/// The item was moved to the Right.
|
||||
/// </summary>
|
||||
Right = 2,
|
||||
}
|
||||
}
|
21
RageCoop.Client/LemonUI/Menus/GridStyle.cs
Normal file
21
RageCoop.Client/LemonUI/Menus/GridStyle.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// The style of the Grid Panel.
|
||||
/// </summary>
|
||||
public enum GridStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// The full grid with X and Y values.
|
||||
/// </summary>
|
||||
Full = 0,
|
||||
/// <summary>
|
||||
/// A single row on the center with the X value only.
|
||||
/// </summary>
|
||||
Row = 1,
|
||||
/// <summary>
|
||||
/// A single column on the center with the Y value only.
|
||||
/// </summary>
|
||||
Column = 2,
|
||||
}
|
||||
}
|
25
RageCoop.Client/LemonUI/Menus/GridValueChangedArgs.cs
Normal file
25
RageCoop.Client/LemonUI/Menus/GridValueChangedArgs.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Previous and Current X and Y values when changing the position on a grid.
|
||||
/// </summary>
|
||||
public class GridValueChangedArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The values present before they were changed.
|
||||
/// </summary>
|
||||
public PointF Before { get; }
|
||||
/// <summary>
|
||||
/// The values present after they were changed.
|
||||
/// </summary>
|
||||
public PointF After { get; }
|
||||
|
||||
internal GridValueChangedArgs(PointF before, PointF after)
|
||||
{
|
||||
Before = before;
|
||||
After = after;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the method that is called when the value on a grid is changed.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">An <see cref="ItemActivatedArgs"/> with the item information.</param>
|
||||
public delegate void GridValueChangedEventHandler(object sender, GridValueChangedArgs e);
|
||||
}
|
18
RageCoop.Client/LemonUI/Menus/ItemActivatedArgs.cs
Normal file
18
RageCoop.Client/LemonUI/Menus/ItemActivatedArgs.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the arguments of an item activation.
|
||||
/// </summary>
|
||||
public class ItemActivatedArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The item that was just activated.
|
||||
/// </summary>
|
||||
public NativeItem Item { get; }
|
||||
|
||||
internal ItemActivatedArgs(NativeItem item)
|
||||
{
|
||||
Item = item;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the method that is called when an item is activated on a menu.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">An <see cref="ItemActivatedArgs"/> with the item information.</param>
|
||||
public delegate void ItemActivatedEventHandler(object sender, ItemActivatedArgs e);
|
||||
}
|
29
RageCoop.Client/LemonUI/Menus/ItemChangedEventArgs.cs
Normal file
29
RageCoop.Client/LemonUI/Menus/ItemChangedEventArgs.cs
Normal file
@ -0,0 +1,29 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the change of the selection of an item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object that got changed.</typeparam>
|
||||
public class ItemChangedEventArgs<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The new object.
|
||||
/// </summary>
|
||||
public T Object { get; set; }
|
||||
/// <summary>
|
||||
/// The index of the object.
|
||||
/// </summary>
|
||||
public int Index { get; }
|
||||
/// <summary>
|
||||
/// The direction of the Item Changed event.
|
||||
/// </summary>
|
||||
public Direction Direction { get; }
|
||||
|
||||
internal ItemChangedEventArgs(T obj, int index, Direction direction)
|
||||
{
|
||||
Object = obj;
|
||||
Index = index;
|
||||
Direction = direction;
|
||||
}
|
||||
}
|
||||
}
|
10
RageCoop.Client/LemonUI/Menus/ItemChangedEventHandler.cs
Normal file
10
RageCoop.Client/LemonUI/Menus/ItemChangedEventHandler.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the method that is called when the selected item is changed on a List Item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of item that was changed.</typeparam>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">A <see cref="ItemChangedEventArgs{T}"/> with the information of the selected item.</param>
|
||||
public delegate void ItemChangedEventHandler<T>(object sender, ItemChangedEventArgs<T> e);
|
||||
}
|
17
RageCoop.Client/LemonUI/Menus/ItemOperation.cs
Normal file
17
RageCoop.Client/LemonUI/Menus/ItemOperation.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// The operation performed when the menu items are modified.
|
||||
/// </summary>
|
||||
public enum ItemOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// The item has been removed.
|
||||
/// </summary>
|
||||
Removed = -1,
|
||||
/// <summary>
|
||||
/// The item has been added.
|
||||
/// </summary>
|
||||
Added = 0,
|
||||
}
|
||||
}
|
36
RageCoop.Client/LemonUI/Menus/MenuModifiedEventArgs.cs
Normal file
36
RageCoop.Client/LemonUI/Menus/MenuModifiedEventArgs.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the different
|
||||
/// </summary>
|
||||
public class MenuModifiedEventArgs
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The item that was modified.
|
||||
/// </summary>
|
||||
public NativeItem Item { get; }
|
||||
/// <summary>
|
||||
/// The operation that was performed in the item.
|
||||
/// </summary>
|
||||
public ItemOperation Operation { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MenuModifiedEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The item that was modified.</param>
|
||||
/// <param name="operation">The operation that was performed in the item.</param>
|
||||
public MenuModifiedEventArgs(NativeItem item, ItemOperation operation)
|
||||
{
|
||||
Item = item;
|
||||
Operation = operation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the method that is called when the items on a menu are changed (added or removed).
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">An <see cref="MenuModifiedEventArgs"/> with the menu operation.</param>
|
||||
public delegate void MenuModifiedEventHandler(object sender, MenuModifiedEventArgs e);
|
||||
}
|
174
RageCoop.Client/LemonUI/Menus/NativeCheckboxItem.cs
Normal file
174
RageCoop.Client/LemonUI/Menus/NativeCheckboxItem.cs
Normal file
@ -0,0 +1,174 @@
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Rockstar-like checkbox item.
|
||||
/// </summary>
|
||||
public class NativeCheckboxItem : NativeItem
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// The image shown on the checkbox.
|
||||
/// </summary>
|
||||
internal protected ScaledTexture check = new ScaledTexture(PointF.Empty, SizeF.Empty, "commonmenu", string.Empty);
|
||||
/// <summary>
|
||||
/// If this item is checked or not.
|
||||
/// </summary>
|
||||
private bool checked_ = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// If this item is checked or not.
|
||||
/// </summary>
|
||||
public bool Checked
|
||||
{
|
||||
get => checked_;
|
||||
set
|
||||
{
|
||||
if (checked_ == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
checked_ = value;
|
||||
UpdateTexture(lastSelected);
|
||||
CheckboxChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the checkbox changes.
|
||||
/// </summary>
|
||||
public event EventHandler CheckboxChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeCheckboxItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title used for the Item.</param>
|
||||
public NativeCheckboxItem(string title) : this(title, string.Empty, false)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeCheckboxItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title used for the Item.</param>
|
||||
/// <param name="check">If the checkbox should be enabled or not.</param>
|
||||
public NativeCheckboxItem(string title, bool check) : this(title, string.Empty, check)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeCheckboxItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title used for the Item.</param>
|
||||
/// <param name="description">The description of the Item.</param>
|
||||
public NativeCheckboxItem(string title, string description) : this(title, description, false)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeCheckboxItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title used for the Item.</param>
|
||||
/// <param name="description">The description of the Item.</param>
|
||||
/// <param name="check">If the checkbox should be enabled or not.</param>
|
||||
public NativeCheckboxItem(string title, string description, bool check) : base(title, description)
|
||||
{
|
||||
Checked = check;
|
||||
Activated += Toggle;
|
||||
EnabledChanged += NativeCheckboxItem_EnabledChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Local Events
|
||||
|
||||
private void NativeCheckboxItem_EnabledChanged(object sender, EventArgs e) => UpdateTexture(lastSelected);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
/// <summary>
|
||||
/// Inverts the checkbox activation.
|
||||
/// </summary>
|
||||
private void Toggle(object sender, EventArgs e) => Checked = !Checked;
|
||||
/// <summary>
|
||||
/// Updates the texture of the sprite.
|
||||
/// </summary>
|
||||
internal protected void UpdateTexture(bool selected)
|
||||
{
|
||||
// If the item is not selected or is not enabled, use the white pictures
|
||||
if (!selected || !Enabled)
|
||||
{
|
||||
check.Texture = Checked ? "shop_box_tick" : "shop_box_blank";
|
||||
}
|
||||
// Otherwise, use the black ones
|
||||
else
|
||||
{
|
||||
check.Texture = Checked ? "shop_box_tickb" : "shop_box_blankb";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the item positions and sizes with the specified values.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the item.</param>
|
||||
/// <param name="size">The size of the item.</param>
|
||||
/// <param name="selected">If this item has been selected.</param>
|
||||
public override void Recalculate(PointF pos, SizeF size, bool selected)
|
||||
{
|
||||
base.Recalculate(pos, size, selected);
|
||||
// Set the correct texture
|
||||
UpdateTexture(selected);
|
||||
// And set the checkbox positions
|
||||
check.Position = new PointF(pos.X + size.Width - 50, pos.Y - 6);
|
||||
check.Size = new SizeF(50, 50);
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the Checkbox on the screen.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
title.Draw();
|
||||
badgeLeft?.Draw();
|
||||
check.Draw();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
base.UpdateColors();
|
||||
|
||||
if (!Enabled)
|
||||
{
|
||||
check.Color = Colors.BadgeRightDisabled;
|
||||
}
|
||||
else if (lastSelected)
|
||||
{
|
||||
check.Color = Colors.BadgeRightHovered;
|
||||
}
|
||||
else
|
||||
{
|
||||
check.Color = Colors.BadgeRightNormal;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
49
RageCoop.Client/LemonUI/Menus/NativeColorData.cs
Normal file
49
RageCoop.Client/LemonUI/Menus/NativeColorData.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Drawing;
|
||||
using LemonUI.Elements;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Color Information shown on the Panel.
|
||||
/// </summary>
|
||||
public class NativeColorData
|
||||
{
|
||||
#region Internal Fields
|
||||
|
||||
internal readonly ScaledRectangle rectangle = new ScaledRectangle(PointF.Empty, SizeF.Empty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The name of the color.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// The RGBA values of the color.
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get => rectangle.Color;
|
||||
set => rectangle.Color = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Color Panel information.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the color.</param>
|
||||
/// <param name="color">The RGBA values of the color.</param>
|
||||
public NativeColorData(string name, Color color)
|
||||
{
|
||||
Name = name;
|
||||
rectangle.Color = color;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
678
RageCoop.Client/LemonUI/Menus/NativeColorPanel.cs
Normal file
678
RageCoop.Client/LemonUI/Menus/NativeColorPanel.cs
Normal file
@ -0,0 +1,678 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.UI;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Control = Rage.GameControl;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
using GTA.UI;
|
||||
#endif
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// A Panel that allows you to select a Color.
|
||||
/// </summary>
|
||||
public class NativeColorPanel : NativePanel
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// The space difference for the colors and opacity bar on the left.
|
||||
/// </summary>
|
||||
private const float leftDifference = 16;
|
||||
/// <summary>
|
||||
/// The space difference for the colors and opacity bar on the left.
|
||||
/// </summary>
|
||||
private const float rightDifference = 12;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// The position reported after the last Recalculation.
|
||||
/// </summary>
|
||||
private PointF lastPosition = PointF.Empty;
|
||||
/// <summary>
|
||||
/// The Width reported after the last Recalculation.
|
||||
/// </summary>
|
||||
private float lastWidth = 0;
|
||||
/// <summary>
|
||||
/// The title of the Color Panel.
|
||||
/// </summary>
|
||||
private ScaledText title = new ScaledText(PointF.Empty, string.Empty, 0.325f)
|
||||
{
|
||||
Alignment = Alignment.Center
|
||||
};
|
||||
/// <summary>
|
||||
/// The rectangle used for marking the item selection on the screen.
|
||||
/// </summary>
|
||||
private ScaledRectangle selectionRectangle = new ScaledRectangle(PointF.Empty, SizeF.Empty);
|
||||
/// <summary>
|
||||
/// The "Opacity" text when the opacity bar is enabled
|
||||
/// </summary>
|
||||
private ScaledText opacityText = new ScaledText(PointF.Empty, "Opacity", 0.325f)
|
||||
{
|
||||
Alignment = Alignment.Center
|
||||
};
|
||||
/// <summary>
|
||||
/// The zero percent text when the opacity bar is enabled.
|
||||
/// </summary>
|
||||
private ScaledText percentMin = new ScaledText(PointF.Empty, "0%", 0.325f);
|
||||
/// <summary>
|
||||
/// The 100 percent text when the opacity bar is enabled.
|
||||
/// </summary>
|
||||
private ScaledText percentMax = new ScaledText(PointF.Empty, "100%", 0.325f);
|
||||
/// <summary>
|
||||
/// The top section of the opacity bar.
|
||||
/// </summary>
|
||||
private ScaledRectangle opacityForeground = new ScaledRectangle(PointF.Empty, SizeF.Empty)
|
||||
{
|
||||
Color = Color.FromArgb(255, 240, 240, 240)
|
||||
};
|
||||
/// <summary>
|
||||
/// The background of the opacity bar.
|
||||
/// </summary>
|
||||
private ScaledRectangle opacityBackground = new ScaledRectangle(PointF.Empty, SizeF.Empty)
|
||||
{
|
||||
Color = Color.FromArgb(150, 88, 88, 88)
|
||||
};
|
||||
/// <summary>
|
||||
/// If the opacity bar is available to the user.
|
||||
/// </summary>
|
||||
private bool showOpacity = false;
|
||||
/// <summary>
|
||||
/// The current value of the opacity slider.
|
||||
/// </summary>
|
||||
private int opacity = 0;
|
||||
/// <summary>
|
||||
/// The current index of the Colors.
|
||||
/// </summary>
|
||||
private int index = 0;
|
||||
/// <summary>
|
||||
/// The position of the first item.
|
||||
/// </summary>
|
||||
private int firstItem = 0;
|
||||
/// <summary>
|
||||
/// The maximum number of items shown at once.
|
||||
/// </summary>
|
||||
private int maxItems = 9;
|
||||
/// <summary>
|
||||
/// The generic title for this color.
|
||||
/// </summary>
|
||||
private string simpleTitle = "Color";
|
||||
/// <summary>
|
||||
/// The style of the title.
|
||||
/// </summary>
|
||||
private ColorTitleStyle titleStyle = ColorTitleStyle.Simple;
|
||||
/// <summary>
|
||||
/// If the number of colors should be shown.
|
||||
/// </summary>
|
||||
private bool showCount = true;
|
||||
/// <summary>
|
||||
/// The items that are currently visible on the screen.
|
||||
/// </summary>
|
||||
private List<NativeColorData> visibleItems = new List<NativeColorData>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Fields
|
||||
|
||||
/// <summary>
|
||||
/// The default sound used for the Color Navigation.
|
||||
/// </summary>
|
||||
public static readonly Sound DefaultSound = new Sound("HUD_FRONTEND_DEFAULT_SOUNDSET", "NAV_LEFT_RIGHT");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Clickable => true;
|
||||
/// <summary>
|
||||
/// If the Opacity selector should be shown.
|
||||
/// </summary>
|
||||
public bool ShowOpacity
|
||||
{
|
||||
get => showOpacity;
|
||||
set
|
||||
{
|
||||
showOpacity = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The opacity value of the color.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The value needs to be set between 100 and 0.
|
||||
/// It will return -1 if <see cref="ShowOpacity"/> is set to <see langword="false"/>.
|
||||
/// </remarks>
|
||||
public int Opacity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!ShowOpacity)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return opacity;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!ShowOpacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (value > 100 || value < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("The value needs to be over 0 and under 100.");
|
||||
}
|
||||
opacity = value;
|
||||
UpdateOpacityBar();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The currently selected color.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If <see cref="ShowOpacity"/> is set to <see langword="true"/>.
|
||||
/// </remarks>
|
||||
public Color SelectedColor
|
||||
{
|
||||
get
|
||||
{
|
||||
// If there is no selected color information, return
|
||||
NativeColorData data = SelectedItem;
|
||||
if (data == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
// Otherwise, return the color
|
||||
return Color.FromArgb(ShowOpacity ? (int)(255 * (Opacity * 0.01f)) : 255, data.Color.R, data.Color.G, data.Color.B);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the currently selected <see cref="NativeColorData"/>.
|
||||
/// </summary>
|
||||
public NativeColorData SelectedItem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Colors.Count == 0 || index >= Colors.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Colors[SelectedIndex];
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The index of the currently selected Color.
|
||||
/// </summary>
|
||||
public int SelectedIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Colors.Count == 0 || index >= Colors.Count)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
set
|
||||
{
|
||||
// If the list of items is empty, don't allow the user to set the index
|
||||
if (Colors == null || Colors.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("There are no items in this menu.");
|
||||
}
|
||||
// If the value is over or equal than the number of items, raise an exception
|
||||
else if (value >= Colors.Count)
|
||||
{
|
||||
throw new InvalidOperationException($"The index is over {Colors.Count - 1}");
|
||||
}
|
||||
|
||||
// Calculate the bounds of the menu
|
||||
int lower = firstItem;
|
||||
int upper = firstItem + maxItems;
|
||||
|
||||
// Time to set the first item based on the total number of items
|
||||
// If the item is between the allowed values, do nothing because we are on the correct first item
|
||||
if (value >= lower && value < upper - 1)
|
||||
{
|
||||
}
|
||||
// If the upper bound + 1 equals the new index, increase it by one
|
||||
else if (upper == value)
|
||||
{
|
||||
firstItem += 1;
|
||||
}
|
||||
// If the first item minus one equals the value, decrease it by one
|
||||
else if (lower - 1 == value)
|
||||
{
|
||||
firstItem -= 1;
|
||||
}
|
||||
// Otherwise, set it somewhere
|
||||
else
|
||||
{
|
||||
// If the value is under the max items, set it to zero
|
||||
if (value < maxItems)
|
||||
{
|
||||
firstItem = 0;
|
||||
}
|
||||
// Otherwise, set it at the bottom
|
||||
else
|
||||
{
|
||||
firstItem = value - maxItems + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the index and update the items
|
||||
index = value;
|
||||
UpdateItems();
|
||||
// Finally, play the switch change sound
|
||||
Sound?.PlayFrontend();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Title used for the Panel when <see cref="TitleStyle"/> is set to <see cref="ColorTitleStyle.Simple"/>.
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get => simpleTitle;
|
||||
set
|
||||
{
|
||||
simpleTitle = value;
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The style of the Panel Title.
|
||||
/// </summary>
|
||||
public ColorTitleStyle TitleStyle
|
||||
{
|
||||
get => titleStyle;
|
||||
set
|
||||
{
|
||||
titleStyle = value;
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// If the count of items should be shown as part of the title.
|
||||
/// </summary>
|
||||
public bool ShowCount
|
||||
{
|
||||
get => showCount;
|
||||
set
|
||||
{
|
||||
showCount = value;
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// THe maximum number of items shown on the screen.
|
||||
/// </summary>
|
||||
public int MaxItems
|
||||
{
|
||||
get => maxItems;
|
||||
set
|
||||
{
|
||||
if (value == maxItems)
|
||||
{
|
||||
return;
|
||||
}
|
||||
maxItems = value;
|
||||
UpdateItems();
|
||||
UpdateTitle();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The colors shown on this Panel.
|
||||
/// </summary>
|
||||
public List<NativeColorData> Colors { get; } = new List<NativeColorData>();
|
||||
/// <summary>
|
||||
/// The sound played when the item is changed.
|
||||
/// </summary>
|
||||
public Sound Sound { get; set; } = DefaultSound;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a color panel with no Items or Title.
|
||||
/// </summary>
|
||||
public NativeColorPanel() : this(string.Empty)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a Panel with a specific Title and set of Colors.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the panel.</param>
|
||||
/// <param name="colors">The colors of the panel.</param>
|
||||
public NativeColorPanel(string title, params NativeColorData[] colors)
|
||||
{
|
||||
// Set the title of the Panel
|
||||
Title = title;
|
||||
// Add the colors that we got
|
||||
Colors.AddRange(colors);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Functions
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Text of the Title.
|
||||
/// </summary>
|
||||
private void UpdateTitle()
|
||||
{
|
||||
string newTitle = string.Empty;
|
||||
|
||||
// Add the title based on the correct style
|
||||
switch (titleStyle)
|
||||
{
|
||||
case ColorTitleStyle.Simple:
|
||||
newTitle = Title;
|
||||
break;
|
||||
case ColorTitleStyle.ColorName:
|
||||
newTitle = SelectedItem == null ? string.Empty : SelectedItem.Name;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we need to add the count of colors
|
||||
if (ShowCount)
|
||||
{
|
||||
// Add a space at the end if required
|
||||
if (!newTitle.EndsWith(" "))
|
||||
{
|
||||
newTitle += " ";
|
||||
}
|
||||
|
||||
// And add the item count
|
||||
newTitle += $"({SelectedIndex + 1} of {Colors.Count})";
|
||||
}
|
||||
|
||||
// And finally set the new title
|
||||
title.Text = newTitle;
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the position of the Items.
|
||||
/// </summary>
|
||||
private void UpdateItems()
|
||||
{
|
||||
// See UpdateItemList() on LemonUI.Menus.NativeMenu to understand this section
|
||||
List<NativeColorData> list = new List<NativeColorData>();
|
||||
|
||||
for (int i = 0; i < MaxItems; i++)
|
||||
{
|
||||
int start = firstItem + i;
|
||||
|
||||
if (start >= Colors.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
list.Add(Colors[start]);
|
||||
}
|
||||
|
||||
visibleItems = list;
|
||||
|
||||
// Get the width based on the maximum number of items
|
||||
float width = (lastWidth - leftDifference - rightDifference) / maxItems;
|
||||
// And store the number of items already completed
|
||||
int count = 0;
|
||||
|
||||
// Select the correct extra distance based on the prescence of the Opacity toggle
|
||||
float extra = ShowOpacity ? 78 : 0;
|
||||
|
||||
// Then, start iterating over the colors visible on the screen
|
||||
foreach (NativeColorData color in visibleItems)
|
||||
{
|
||||
// Set the position based on the number of items completed
|
||||
color.rectangle.Position = new PointF(lastPosition.X + leftDifference + (width * count), lastPosition.Y + extra + 54);
|
||||
// And set the size of it based on the number of items
|
||||
color.rectangle.Size = new SizeF(width, 45);
|
||||
// Finally, increase the count by one
|
||||
count++;
|
||||
}
|
||||
|
||||
// If there is a selected color item
|
||||
if (SelectedItem != null)
|
||||
{
|
||||
// Set the position and size of the selection rectangle based on the currently selected color
|
||||
ScaledRectangle colorRect = SelectedItem.rectangle;
|
||||
const float height = 8;
|
||||
selectionRectangle.Position = new PointF(colorRect.Position.X, colorRect.Position.Y - height);
|
||||
selectionRectangle.Size = new SizeF(colorRect.Size.Width, height);
|
||||
}
|
||||
|
||||
// Finally, update the text of the title
|
||||
UpdateTitle();
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the size of the opacity bar.
|
||||
/// </summary>
|
||||
private void UpdateOpacityBar()
|
||||
{
|
||||
// If the opacity bar is disabled, return
|
||||
if (!ShowOpacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, set the size based in the last known position
|
||||
float x = lastPosition.X + 13;
|
||||
float y = lastPosition.Y + 48;
|
||||
float width = lastWidth - leftDifference - rightDifference;
|
||||
const float height = 9;
|
||||
opacityBackground.Position = new PointF(x, y);
|
||||
opacityBackground.Size = new SizeF(width, height);
|
||||
opacityForeground.Position = new PointF(x, y);
|
||||
opacityForeground.Size = new SizeF(width * (Opacity * 0.01f), height);
|
||||
}
|
||||
/// <summary>
|
||||
/// Recalculates the Color panel with the last known Position and Width.
|
||||
/// </summary>
|
||||
private void Recalculate() => Recalculate(lastPosition, lastWidth);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Moves to the Previous Color.
|
||||
/// </summary>
|
||||
public void Previous()
|
||||
{
|
||||
// If there are no items, return
|
||||
if (Colors.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are on the first item, go back to the last one
|
||||
if (SelectedIndex == 0)
|
||||
{
|
||||
SelectedIndex = Colors.Count - 1;
|
||||
}
|
||||
// Otherwise, reduce it by one
|
||||
else
|
||||
{
|
||||
SelectedIndex -= 1;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Moves to the Next Color.
|
||||
/// </summary>
|
||||
public void Next()
|
||||
{
|
||||
// If there are no items, return
|
||||
if (Colors.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are on the last item, go back to the first one
|
||||
if (Colors.Count - 1 == SelectedIndex)
|
||||
{
|
||||
SelectedIndex = 0;
|
||||
}
|
||||
// Otherwise, increase it by one
|
||||
else
|
||||
{
|
||||
SelectedIndex += 1;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a color to the Panel.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to add.</param>
|
||||
public void Add(NativeColorData color)
|
||||
{
|
||||
if (Colors.Contains(color))
|
||||
{
|
||||
throw new ArgumentException("Color is already part of the Panel.", nameof(color));
|
||||
}
|
||||
Colors.Add(color);
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a color from the panel.
|
||||
/// </summary>
|
||||
/// <param name="color">The color to remove.</param>
|
||||
public void Remove(NativeColorData color)
|
||||
{
|
||||
// Remove it if there
|
||||
// If not, ignore it
|
||||
Colors.Remove(color);
|
||||
// If the index is higher or equal than the max number of items
|
||||
// Set the max - 1
|
||||
if (SelectedIndex >= Colors.Count)
|
||||
{
|
||||
SelectedIndex = Colors.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateItems();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the
|
||||
/// </summary>
|
||||
/// <param name="func"></param>
|
||||
public void Remove(Func<NativeColorData, bool> func)
|
||||
{
|
||||
foreach (NativeColorData color in new List<NativeColorData>(Colors))
|
||||
{
|
||||
if (func(color))
|
||||
{
|
||||
Colors.Remove(color);
|
||||
}
|
||||
}
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the colors from the Panel.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Colors.Clear();
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks if the Color Data is present on this Panel.
|
||||
/// </summary>
|
||||
/// <param name="color">The Color Data to check.</param>
|
||||
public void Contains(NativeColorData color) => Colors.Contains(color);
|
||||
/// <summary>
|
||||
/// Recalculates the position of the Color Panel.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the panel.</param>
|
||||
/// <param name="width">The width of the menu.</param>
|
||||
public override void Recalculate(PointF position, float width)
|
||||
{
|
||||
// Save the last position and width
|
||||
lastPosition = position;
|
||||
lastWidth = width;
|
||||
|
||||
// Select the correct extra distance based on the prescence of the Opacity toggle
|
||||
float extra = ShowOpacity ? 78 : 0;
|
||||
|
||||
// Set the position and size of the Background
|
||||
Background.Position = position;
|
||||
Background.Size = new SizeF(width, ShowOpacity ? 188 : 111);
|
||||
// And then set the position of the text
|
||||
title.Position = new PointF(position.X + (width * 0.5f), position.Y + extra + 10f);
|
||||
// Then, set the position of the opacity bar and texts
|
||||
UpdateOpacityBar();
|
||||
opacityText.Position = new PointF(position.X + (width * 0.5f), position.Y + 10f);
|
||||
percentMin.Position = new PointF(position.X + 9, position.Y + 11);
|
||||
percentMax.Position = new PointF(position.X + width - 60, position.Y + 11);
|
||||
|
||||
// Finally, update the list of items
|
||||
UpdateItems();
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the Color Panel.
|
||||
/// </summary>
|
||||
public override void Process()
|
||||
{
|
||||
// If the user pressed one of the keys, move to the left or right
|
||||
if (Controls.IsJustPressed(Control.FrontendLt))
|
||||
{
|
||||
Previous();
|
||||
}
|
||||
else if (Controls.IsJustPressed(Control.FrontendRt))
|
||||
{
|
||||
Next();
|
||||
}
|
||||
// If the user pressed one of the bumpers with the Opacity bar enabled, increase or decrease it
|
||||
else if (ShowOpacity && Controls.IsJustPressed(Control.FrontendLb))
|
||||
{
|
||||
if (Opacity > 0)
|
||||
{
|
||||
Opacity--;
|
||||
Sound?.PlayFrontend();
|
||||
}
|
||||
}
|
||||
else if (ShowOpacity && Controls.IsJustPressed(Control.FrontendRb))
|
||||
{
|
||||
if (Opacity < 100)
|
||||
{
|
||||
Opacity++;
|
||||
Sound?.PlayFrontend();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the items
|
||||
base.Process();
|
||||
title.Draw();
|
||||
foreach (NativeColorData color in visibleItems)
|
||||
{
|
||||
color.rectangle.Draw();
|
||||
}
|
||||
if (Colors.Count != 0)
|
||||
{
|
||||
selectionRectangle.Draw();
|
||||
}
|
||||
if (ShowOpacity)
|
||||
{
|
||||
opacityText.Draw();
|
||||
percentMin.Draw();
|
||||
percentMax.Draw();
|
||||
opacityBackground.Draw();
|
||||
opacityForeground.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
163
RageCoop.Client/LemonUI/Menus/NativeDynamicItem.cs
Normal file
163
RageCoop.Client/LemonUI/Menus/NativeDynamicItem.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using LemonUI.Elements;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Dynamic Items allow you to dynamically change the item shown to the user.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of item.</typeparam>
|
||||
public class NativeDynamicItem<T> : NativeSlidableItem
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly ScaledText text = new ScaledText(PointF.Empty, string.Empty, 0.35f);
|
||||
private T item = default;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected item.
|
||||
/// </summary>
|
||||
public T SelectedItem
|
||||
{
|
||||
get => item;
|
||||
set
|
||||
{
|
||||
item = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the user has changed the item.
|
||||
/// </summary>
|
||||
public event ItemChangedEventHandler<T> ItemChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Dynamic List Item.
|
||||
/// </summary>
|
||||
/// <param name="title">The Title of the item.</param>
|
||||
public NativeDynamicItem(string title) : this(title, string.Empty, default)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new Dynamic List Item.
|
||||
/// </summary>
|
||||
/// <param name="title">The Title of the item.</param>
|
||||
/// <param name="item">The Item to set.</param>
|
||||
public NativeDynamicItem(string title, T item) : this(title, string.Empty, item)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new Dynamic List Item.
|
||||
/// </summary>
|
||||
/// <param name="title">The Title of the item.</param>
|
||||
/// <param name="description">The Description of the item.</param>
|
||||
public NativeDynamicItem(string title, string description) : this(title, description, default)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new Dynamic List Item.
|
||||
/// </summary>
|
||||
/// <param name="title">The Title of the item.</param>
|
||||
/// <param name="description">The Description of the item.</param>
|
||||
/// <param name="item">The Item to set.</param>
|
||||
public NativeDynamicItem(string title, string description, T item) : base(title, description)
|
||||
{
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Updates the currently selected item based on the index.
|
||||
/// </summary>
|
||||
private void UpdateItemName()
|
||||
{
|
||||
// This is the SAME as the normal NativeListItem
|
||||
|
||||
text.Text = !SelectedItem.Equals(default) ? SelectedItem.ToString() : string.Empty;
|
||||
|
||||
text.Position = new PointF(RightArrow.Position.X - text.Width + 3, text.Position.Y);
|
||||
LeftArrow.Position = new PointF(text.Position.X - LeftArrow.Size.Width, LeftArrow.Position.Y);
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the previous item.
|
||||
/// </summary>
|
||||
public override void GoLeft()
|
||||
{
|
||||
ItemChangedEventArgs<T> arguments = new ItemChangedEventArgs<T>(item, -1, Direction.Left);
|
||||
ItemChanged?.Invoke(this, arguments);
|
||||
SelectedItem = arguments.Object;
|
||||
UpdateItemName();
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the next item.
|
||||
/// </summary>
|
||||
public override void GoRight()
|
||||
{
|
||||
ItemChangedEventArgs<T> arguments = new ItemChangedEventArgs<T>(item, -1, Direction.Right);
|
||||
ItemChanged?.Invoke(this, arguments);
|
||||
SelectedItem = arguments.Object;
|
||||
UpdateItemName();
|
||||
}
|
||||
/// <summary>
|
||||
/// Recalculates the position of the current List Item.
|
||||
/// </summary>
|
||||
/// <param name="pos">The new position of the item.</param>
|
||||
/// <param name="size">The Size of the item.</param>
|
||||
/// <param name="selected">If the item is selected or not.</param>
|
||||
public override void Recalculate(PointF pos, SizeF size, bool selected)
|
||||
{
|
||||
// This is the SAME as the normal NativeListItem
|
||||
|
||||
base.Recalculate(pos, size, selected);
|
||||
|
||||
float textWidth = RightArrow.Size.Width;
|
||||
text.Position = new PointF(pos.X + size.Width - textWidth - 1 - text.Width, pos.Y + 3);
|
||||
LeftArrow.Position = new PointF(text.Position.X - LeftArrow.Size.Width, pos.Y + 4);
|
||||
|
||||
UpdateItemName();
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the List on the screen.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw(); // Arrows, Title and Left Badge
|
||||
text.Draw();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
base.UpdateColors();
|
||||
|
||||
if (!Enabled)
|
||||
{
|
||||
text.Color = Colors.TitleDisabled;
|
||||
}
|
||||
else if (lastSelected)
|
||||
{
|
||||
text.Color = Colors.TitleHovered;
|
||||
}
|
||||
else
|
||||
{
|
||||
text.Color = Colors.TitleNormal;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
371
RageCoop.Client/LemonUI/Menus/NativeGridPanel.cs
Normal file
371
RageCoop.Client/LemonUI/Menus/NativeGridPanel.cs
Normal file
@ -0,0 +1,371 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.Native;
|
||||
using CitizenFX.Core.UI;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage;
|
||||
using Rage.Native;
|
||||
using Control = Rage.GameControl;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
#endif
|
||||
using LemonUI.Elements;
|
||||
using LemonUI.Extensions;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a grid where you can select X and Y values.
|
||||
/// </summary>
|
||||
public class NativeGridPanel : NativePanel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private PointF position = PointF.Empty;
|
||||
private float width = 0;
|
||||
|
||||
private readonly ScaledText labelTop = new ScaledText(PointF.Empty, "Y+", 0.33f)
|
||||
{
|
||||
Alignment = Alignment.Center
|
||||
};
|
||||
private readonly ScaledText labelBottom = new ScaledText(PointF.Empty, "Y-", 0.33f)
|
||||
{
|
||||
Alignment = Alignment.Center
|
||||
};
|
||||
private readonly ScaledText labelLeft = new ScaledText(PointF.Empty, "X-", 0.33f)
|
||||
{
|
||||
Alignment = Alignment.Right
|
||||
};
|
||||
private readonly ScaledText labelRight = new ScaledText(PointF.Empty, "X+", 0.33f);
|
||||
private readonly ScaledTexture grid = new ScaledTexture("pause_menu_pages_char_mom_dad", "nose_grid")
|
||||
{
|
||||
Color = Color.FromArgb(205, 105, 105, 102)
|
||||
};
|
||||
private readonly ScaledTexture dot = new ScaledTexture("commonmenu", "common_medal")
|
||||
{
|
||||
Color = Color.FromArgb(255, 255, 255, 255)
|
||||
};
|
||||
private PointF innerPosition = PointF.Empty;
|
||||
private SizeF innerSize = SizeF.Empty;
|
||||
private GridStyle style = GridStyle.Full;
|
||||
private float x;
|
||||
private float y;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Clickable => true;
|
||||
/// <summary>
|
||||
/// The X value between 0 and 1.
|
||||
/// </summary>
|
||||
public float X
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
case GridStyle.Full:
|
||||
case GridStyle.Row:
|
||||
return x;
|
||||
default:
|
||||
return 0.5f;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 1 || value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
|
||||
if (style == GridStyle.Column)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PointF before = new PointF(X, Y);
|
||||
x = value;
|
||||
UpdateDot(before);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The X value between 0 and 1.
|
||||
/// </summary>
|
||||
public float Y
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
case GridStyle.Full:
|
||||
case GridStyle.Column:
|
||||
return y;
|
||||
default:
|
||||
return 0.5f;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 1 || value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
|
||||
if (style == GridStyle.Row)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PointF before = new PointF(X, Y);
|
||||
y = value;
|
||||
UpdateDot(before);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The text label shown on the top.
|
||||
/// </summary>
|
||||
public string LabelTop
|
||||
{
|
||||
get => labelTop.Text;
|
||||
set => labelTop.Text = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The text label shown on the bottom.
|
||||
/// </summary>
|
||||
public string LabelBottom
|
||||
{
|
||||
get => labelBottom.Text;
|
||||
set => labelBottom.Text = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The text label shown on the left.
|
||||
/// </summary>
|
||||
public string LabelLeft
|
||||
{
|
||||
get => labelLeft.Text;
|
||||
set => labelLeft.Text = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The text label shown on the right.
|
||||
/// </summary>
|
||||
public string LabelRight
|
||||
{
|
||||
get => labelRight.Text;
|
||||
set => labelRight.Text = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The style of this grid.
|
||||
/// </summary>
|
||||
public GridStyle Style
|
||||
{
|
||||
get => style;
|
||||
set
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(GridStyle), value))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "The Grid style is not valid! Expected Full, Row or Column.");
|
||||
}
|
||||
|
||||
style = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when X and/or Y values are changed.
|
||||
/// </summary>
|
||||
public event GridValueChangedEventHandler ValuesChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeGridPanel"/>.
|
||||
/// </summary>
|
||||
public NativeGridPanel() : base()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
private void Recalculate() => Recalculate(position, width);
|
||||
private void UpdateDot(PointF before, bool trigger = true)
|
||||
{
|
||||
float posX = innerSize.Width * (style == GridStyle.Full || style == GridStyle.Row ? x : 0.5f);
|
||||
float posY = innerSize.Height * (style == GridStyle.Full || style == GridStyle.Column ? y : 0.5f);
|
||||
|
||||
dot.Size = new SizeF(45, 45);
|
||||
dot.Position = new PointF(innerPosition.X + posX - (dot.Size.Width * 0.5f),
|
||||
innerPosition.Y + posY - (dot.Size.Height * 0.5f));
|
||||
|
||||
if (trigger)
|
||||
{
|
||||
ValuesChanged?.Invoke(this, new GridValueChangedArgs(before, new PointF(X, Y)));
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void Recalculate(PointF position, float width)
|
||||
{
|
||||
this.position = position;
|
||||
this.width = width;
|
||||
|
||||
const float height = 270;
|
||||
const int offsetX = 20;
|
||||
const int offsetY = 20;
|
||||
|
||||
base.Recalculate(position, width);
|
||||
Background.Size = new SizeF(width, height);
|
||||
|
||||
switch (style)
|
||||
{
|
||||
case GridStyle.Full:
|
||||
grid.Position = new PointF(position.X + (width * 0.5f) - 95, position.Y + (height * 0.5f) - 94);
|
||||
grid.Size = new SizeF(192, 192);
|
||||
break;
|
||||
case GridStyle.Row:
|
||||
grid.Position = new PointF(position.X + (width * 0.5f) - 95, position.Y + (height * 0.5f) - 15);
|
||||
grid.Size = new SizeF(192, 36);
|
||||
break;
|
||||
case GridStyle.Column:
|
||||
grid.Position = new PointF(position.X + (width * 0.5f) - 17, position.Y + (height * 0.5f) - 94);
|
||||
grid.Size = new SizeF(36, 192);
|
||||
break;
|
||||
}
|
||||
|
||||
labelTop.Position = new PointF(position.X + (width * 0.5f), position.Y + 10);
|
||||
labelBottom.Position = new PointF(position.X + (width * 0.5f), position.Y + height - 34);
|
||||
labelLeft.Position = new PointF(position.X + (width * 0.5f) - 102, position.Y + (height * 0.5f) - (labelLeft.LineHeight * 0.5f));
|
||||
labelRight.Position = new PointF(position.X + (width * 0.5f) + 102, position.Y + (height * 0.5f) - (labelLeft.LineHeight * 0.5f));
|
||||
|
||||
innerPosition = new PointF(grid.Position.X + offsetX, grid.Position.Y + offsetY);
|
||||
innerSize = new SizeF(grid.Size.Width - (offsetX * 2), grid.Size.Height - (offsetY * 2));
|
||||
|
||||
UpdateDot(PointF.Empty, false);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void Process()
|
||||
{
|
||||
float previousX = X;
|
||||
float previousY = Y;
|
||||
|
||||
Background.Draw();
|
||||
switch (style)
|
||||
{
|
||||
case GridStyle.Full:
|
||||
labelTop.Draw();
|
||||
labelBottom.Draw();
|
||||
labelLeft.Draw();
|
||||
labelRight.Draw();
|
||||
grid.Draw();
|
||||
break;
|
||||
case GridStyle.Row:
|
||||
labelLeft.Draw();
|
||||
labelRight.Draw();
|
||||
grid.DrawSpecific(new PointF(0, 0.4f), new PointF(1, 0.6f));
|
||||
break;
|
||||
case GridStyle.Column:
|
||||
labelTop.Draw();
|
||||
labelBottom.Draw();
|
||||
grid.DrawSpecific(new PointF(0.4f, 0), new PointF(0.6f, 1));
|
||||
break;
|
||||
}
|
||||
dot.Draw();
|
||||
|
||||
#if FIVEM
|
||||
bool usingKeyboard = API.IsInputDisabled(2);
|
||||
#elif RAGEMP
|
||||
bool usingKeyboard = Invoker.Invoke<bool>(0xA571D46727E2B718, 2);
|
||||
#elif RPH
|
||||
bool usingKeyboard = NativeFunction.CallByHash<bool>(0xA571D46727E2B718, 2);
|
||||
#elif SHVDN3
|
||||
bool usingKeyboard = Function.Call<bool>(Hash._IS_USING_KEYBOARD, 2);
|
||||
#endif
|
||||
if (usingKeyboard)
|
||||
{
|
||||
if (Screen.IsCursorInArea(grid.Position, grid.Size) && Controls.IsPressed(Control.CursorAccept))
|
||||
{
|
||||
PointF cursor = Screen.CursorPositionRelative;
|
||||
PointF pos = innerPosition.ToRelative();
|
||||
|
||||
PointF start = new PointF(cursor.X - pos.X, cursor.Y - pos.Y);
|
||||
SizeF size = innerSize.ToRelative();
|
||||
|
||||
x = start.X / size.Width;
|
||||
y = start.Y / size.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Controls.DisableThisFrame(Control.LookUpDown);
|
||||
Controls.DisableThisFrame(Control.LookLeftRight);
|
||||
Controls.EnableThisFrame(Control.ScriptRightAxisX);
|
||||
Controls.EnableThisFrame(Control.ScriptRightAxisY);
|
||||
|
||||
#if FIVEM
|
||||
float rX = Game.GetControlNormal(0, Control.ScriptRightAxisX);
|
||||
float rY = Game.GetControlNormal(0, Control.ScriptRightAxisY);
|
||||
float frameTime = Game.LastFrameTime;
|
||||
#elif RAGEMP
|
||||
float rX = Invoker.Invoke<float>(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisX);
|
||||
float rY = Invoker.Invoke<float>(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisY);
|
||||
float frameTime = Invoker.Invoke<float>(Natives.GetFrameTime);
|
||||
#elif RPH
|
||||
float rX = NativeFunction.CallByHash<float>(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisX);
|
||||
float rY = NativeFunction.CallByHash<float>(0xEC3C9B8D5327B563, 0, (int)Control.ScriptRightAxisY);
|
||||
float frameTime = Game.FrameTime;
|
||||
#elif SHVDN3
|
||||
float rX = Game.GetControlValueNormalized(Control.ScriptRightAxisX);
|
||||
float rY = Game.GetControlValueNormalized(Control.ScriptRightAxisY);
|
||||
float frameTime = Game.LastFrameTime;
|
||||
#endif
|
||||
|
||||
x += rX * frameTime;
|
||||
y += rY * frameTime;
|
||||
}
|
||||
|
||||
// Make sure that the values are not under zero or over one
|
||||
if (x < 0)
|
||||
{
|
||||
x = 0;
|
||||
}
|
||||
else if (x > 1)
|
||||
{
|
||||
x = 1;
|
||||
}
|
||||
if (y < 0)
|
||||
{
|
||||
y = 0;
|
||||
}
|
||||
else if (y > 1)
|
||||
{
|
||||
y = 1;
|
||||
}
|
||||
|
||||
if (previousX != x || previousY != y)
|
||||
{
|
||||
UpdateDot(new PointF(previousX, previousX));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
398
RageCoop.Client/LemonUI/Menus/NativeItem.cs
Normal file
398
RageCoop.Client/LemonUI/Menus/NativeItem.cs
Normal file
@ -0,0 +1,398 @@
|
||||
#if FIVEM
|
||||
using Font = CitizenFX.Core.UI.Font;
|
||||
#elif RAGEMP
|
||||
using Font = RAGE.Game.Font;
|
||||
#elif RPH
|
||||
using Font = LemonUI.Elements.Font;
|
||||
#elif SHVDN3
|
||||
using Font = GTA.UI.Font;
|
||||
#endif
|
||||
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic Rockstar-like item.
|
||||
/// </summary>
|
||||
public class NativeItem : IDrawable
|
||||
{
|
||||
#region Protected Internal Fields
|
||||
|
||||
/// <summary>
|
||||
/// The title of the object.
|
||||
/// </summary>
|
||||
protected internal ScaledText title = null;
|
||||
/// <summary>
|
||||
/// The last known Item Position.
|
||||
/// </summary>
|
||||
protected internal PointF lastPosition = PointF.Empty;
|
||||
/// <summary>
|
||||
/// The last known Item Size.
|
||||
/// </summary>
|
||||
protected internal SizeF lastSize = SizeF.Empty;
|
||||
/// <summary>
|
||||
/// The last known Item Selection.
|
||||
/// </summary>
|
||||
protected internal bool lastSelected = false;
|
||||
/// <summary>
|
||||
/// The left badge of the Item.
|
||||
/// </summary>
|
||||
protected internal I2Dimensional badgeLeft = null;
|
||||
/// <summary>
|
||||
/// The left badge of the Item.
|
||||
/// </summary>
|
||||
protected internal I2Dimensional badgeRight = null;
|
||||
/// <summary>
|
||||
/// The alternate title of the menu.
|
||||
/// </summary>
|
||||
protected internal ScaledText altTitle = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool enabled = true;
|
||||
private BadgeSet badgeSetLeft = null;
|
||||
private BadgeSet badgeSetRight = null;
|
||||
private ColorSet colors = new ColorSet();
|
||||
private ScaledRectangle background = new ScaledRectangle(PointF.Empty, SizeF.Empty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// If this item can be used or not.
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get => enabled;
|
||||
set
|
||||
{
|
||||
if (enabled == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
enabled = value;
|
||||
EnabledChanged?.Invoke(this, EventArgs.Empty);
|
||||
UpdateColors();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Object that contains data about this Item.
|
||||
/// </summary>
|
||||
public virtual object Tag { get; set; }
|
||||
/// <summary>
|
||||
/// The title of the item.
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get => title.Text;
|
||||
set => title.Text = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The alternative title of the item shown on the right.
|
||||
/// </summary>
|
||||
public string AltTitle
|
||||
{
|
||||
get => altTitle.Text;
|
||||
set
|
||||
{
|
||||
altTitle.Text = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The font of title item.
|
||||
/// </summary>
|
||||
public Font TitleFont
|
||||
{
|
||||
get => title.Font;
|
||||
set => title.Font = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The font of alternative title item shown on the right.
|
||||
/// </summary>
|
||||
public Font AltTitleFont
|
||||
{
|
||||
get => altTitle.Font;
|
||||
set => altTitle.Font = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The description of the item.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// The Left badge of the Item.
|
||||
/// </summary>
|
||||
public I2Dimensional LeftBadge
|
||||
{
|
||||
get => badgeLeft;
|
||||
set
|
||||
{
|
||||
badgeLeft = value;
|
||||
Recalculate();
|
||||
UpdateColors();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Left badge set of the Item.
|
||||
/// </summary>
|
||||
public BadgeSet LeftBadgeSet
|
||||
{
|
||||
get => badgeSetLeft;
|
||||
set
|
||||
{
|
||||
badgeSetLeft = value;
|
||||
Recalculate();
|
||||
UpdateColors();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Right badge of the Item.
|
||||
/// </summary>
|
||||
public I2Dimensional RightBadge
|
||||
{
|
||||
get => badgeRight;
|
||||
set
|
||||
{
|
||||
badgeRight = value;
|
||||
Recalculate();
|
||||
UpdateColors();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Right badge set of the Item.
|
||||
/// </summary>
|
||||
public BadgeSet RightBadgeSet
|
||||
{
|
||||
get => badgeSetRight;
|
||||
set
|
||||
{
|
||||
badgeSetRight = value;
|
||||
Recalculate();
|
||||
UpdateColors();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The different colors that change dynamically when the item is used.
|
||||
/// </summary>
|
||||
public ColorSet Colors
|
||||
{
|
||||
get => colors;
|
||||
set
|
||||
{
|
||||
colors = value;
|
||||
UpdateColors();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Panel asociated to this <see cref="NativeItem"/>.
|
||||
/// </summary>
|
||||
public NativePanel Panel { get; set; } = null;
|
||||
/// <summary>
|
||||
/// If a custom colored background should be used.
|
||||
/// </summary>
|
||||
public bool UseCustomBackground { get; set; }
|
||||
/// <summary>
|
||||
/// If this item is being hovered.
|
||||
/// </summary>
|
||||
public bool IsHovered => Screen.IsCursorInArea(background.Position, background.Size);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the item is selected.
|
||||
/// </summary>
|
||||
public event SelectedEventHandler Selected;
|
||||
/// <summary>
|
||||
/// Event triggered when the item is activated.
|
||||
/// </summary>
|
||||
public event EventHandler Activated;
|
||||
/// <summary>
|
||||
/// Event triggered when the <see cref="Enabled"/> property is changed.
|
||||
/// </summary>
|
||||
public event EventHandler EnabledChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the item.</param>
|
||||
public NativeItem(string title) : this(title, string.Empty, string.Empty)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the item.</param>
|
||||
/// <param name="description">The description of the item.</param>
|
||||
public NativeItem(string title, string description) : this(title, description, string.Empty)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the item.</param>
|
||||
/// <param name="description">The description of the item.</param>
|
||||
/// <param name="altTitle">The alternative title of the item, shown on the right.</param>
|
||||
public NativeItem(string title, string description, string altTitle)
|
||||
{
|
||||
this.title = new ScaledText(PointF.Empty, title, 0.345f);
|
||||
Description = description;
|
||||
this.altTitle = new ScaledText(PointF.Empty, altTitle, 0.345f);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Triggers
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the Selected event.
|
||||
/// </summary>
|
||||
protected internal void OnSelected(object sender, SelectedEventArgs e) => Selected?.Invoke(sender, e);
|
||||
/// <summary>
|
||||
/// Triggers the Activated event.
|
||||
/// </summary>
|
||||
protected internal void OnActivated(object sender) => Activated?.Invoke(sender, EventArgs.Empty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the item with the last known values.
|
||||
/// </summary>
|
||||
protected void Recalculate() => Recalculate(lastPosition, lastSize, lastSelected);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the item positions and sizes with the specified values.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the item.</param>
|
||||
/// <param name="size">The size of the item.</param>
|
||||
/// <param name="selected">If this item has been selected.</param>
|
||||
public virtual void Recalculate(PointF pos, SizeF size, bool selected)
|
||||
{
|
||||
lastPosition = pos;
|
||||
lastSize = size;
|
||||
lastSelected = selected;
|
||||
|
||||
background.Position = pos;
|
||||
background.Size = size;
|
||||
|
||||
if (badgeSetLeft != null)
|
||||
{
|
||||
if (!(badgeLeft is ScaledTexture))
|
||||
{
|
||||
badgeLeft = new ScaledTexture(string.Empty, string.Empty);
|
||||
}
|
||||
ScaledTexture left = (ScaledTexture)badgeLeft;
|
||||
left.Dictionary = selected ? badgeSetLeft.HoveredDictionary : badgeSetLeft.NormalDictionary;
|
||||
left.Texture = selected ? badgeSetLeft.HoveredTexture : badgeSetLeft.NormalTexture;
|
||||
}
|
||||
if (badgeSetRight != null)
|
||||
{
|
||||
if (!(badgeRight is ScaledTexture))
|
||||
{
|
||||
badgeRight = new ScaledTexture(string.Empty, string.Empty);
|
||||
}
|
||||
ScaledTexture right = (ScaledTexture)badgeRight;
|
||||
right.Dictionary = selected ? badgeSetRight.HoveredDictionary : badgeSetRight.NormalDictionary;
|
||||
right.Texture = selected ? badgeSetRight.HoveredTexture : badgeSetRight.NormalTexture;
|
||||
}
|
||||
|
||||
if (badgeLeft != null)
|
||||
{
|
||||
badgeLeft.Position = new PointF(pos.X + 2, pos.Y - 3);
|
||||
badgeLeft.Size = new SizeF(45, 45);
|
||||
}
|
||||
if (badgeRight != null)
|
||||
{
|
||||
badgeRight.Position = new PointF(pos.X + size.Width - 47, pos.Y - 3);
|
||||
badgeRight.Size = new SizeF(45, 45);
|
||||
}
|
||||
|
||||
title.Position = new PointF(pos.X + (badgeLeft == null ? 0 : 34) + 6, pos.Y + 3);
|
||||
altTitle.Position = new PointF(pos.X + size.Width - (badgeRight == null ? 0 : 34) - altTitle.Width - 6, pos.Y + 3);
|
||||
|
||||
UpdateColors();
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the item.
|
||||
/// </summary>
|
||||
public virtual void Draw()
|
||||
{
|
||||
if (UseCustomBackground)
|
||||
{
|
||||
background.Draw();
|
||||
}
|
||||
|
||||
title.Draw();
|
||||
altTitle.Draw();
|
||||
badgeLeft?.Draw();
|
||||
badgeRight?.Draw();
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the colors of the <see cref="Elements"/> from the <see cref="Colors"/> <see cref="ColorSet"/>.
|
||||
/// </summary>
|
||||
public virtual void UpdateColors()
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
background.Color = Colors.BackgroundDisabled;
|
||||
title.Color = Colors.TitleDisabled;
|
||||
altTitle.Color = Colors.AltTitleDisabled;
|
||||
if (badgeLeft != null)
|
||||
{
|
||||
badgeLeft.Color = Colors.BadgeLeftDisabled;
|
||||
}
|
||||
if (badgeRight != null)
|
||||
{
|
||||
badgeRight.Color = Colors.BadgeRightDisabled;
|
||||
}
|
||||
}
|
||||
else if (lastSelected)
|
||||
{
|
||||
background.Color = Colors.BackgroundHovered;
|
||||
title.Color = Colors.TitleHovered;
|
||||
altTitle.Color = Colors.AltTitleHovered;
|
||||
if (badgeLeft != null)
|
||||
{
|
||||
badgeLeft.Color = Colors.BadgeLeftHovered;
|
||||
}
|
||||
if (badgeRight != null)
|
||||
{
|
||||
badgeRight.Color = Colors.BadgeRightHovered;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
background.Color = Colors.BackgroundNormal;
|
||||
title.Color = Colors.TitleNormal;
|
||||
altTitle.Color = Colors.AltTitleNormal;
|
||||
if (badgeLeft != null)
|
||||
{
|
||||
badgeLeft.Color = Colors.BadgeLeftNormal;
|
||||
}
|
||||
if (badgeRight != null)
|
||||
{
|
||||
badgeRight.Color = Colors.BadgeRightNormal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
366
RageCoop.Client/LemonUI/Menus/NativeListItem.cs
Normal file
366
RageCoop.Client/LemonUI/Menus/NativeListItem.cs
Normal file
@ -0,0 +1,366 @@
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for list items.
|
||||
/// </summary>
|
||||
public abstract class NativeListItem : NativeSlidableItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The text of the current item.
|
||||
/// </summary>
|
||||
internal protected ScaledText text = null;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new list item with a title and subtitle.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
/// <param name="subtitle">The subtitle of the Item.</param>
|
||||
public NativeListItem(string title, string subtitle) : base(title, subtitle)
|
||||
{
|
||||
text = new ScaledText(PointF.Empty, string.Empty, 0.35f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An item that allows you to scroll between a set of objects.
|
||||
/// </summary>
|
||||
public class NativeListItem<T> : NativeListItem
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private int index = 0;
|
||||
private List<T> items = new List<T>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The index of the currently selected index.
|
||||
/// </summary>
|
||||
public int SelectedIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("There are no available items.");
|
||||
}
|
||||
if (value < 0)
|
||||
{
|
||||
throw new InvalidOperationException("The index is under zero.");
|
||||
}
|
||||
if (value >= Items.Count)
|
||||
{
|
||||
throw new InvalidOperationException($"The index is over the limit of {Items.Count - 1}");
|
||||
}
|
||||
if (index == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
index = value;
|
||||
TriggerEvent(value, Direction.Unknown);
|
||||
UpdateIndex();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The currently selected item.
|
||||
/// </summary>
|
||||
public T SelectedItem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return Items[SelectedIndex];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("There are no available items.");
|
||||
}
|
||||
|
||||
int newIndex = Items.IndexOf(value);
|
||||
|
||||
if (newIndex == -1)
|
||||
{
|
||||
throw new InvalidOperationException("The object is not the list of Items.");
|
||||
}
|
||||
|
||||
SelectedIndex = newIndex;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The objects used by this item.
|
||||
/// </summary>
|
||||
public List<T> Items
|
||||
{
|
||||
get => items;
|
||||
set
|
||||
{
|
||||
items = value;
|
||||
UpdateIndex();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the selected item is changed.
|
||||
/// </summary>
|
||||
public event ItemChangedEventHandler<T> ItemChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeListItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
/// <param name="objs">The objects that are available on the Item.</param>
|
||||
public NativeListItem(string title, params T[] objs) : this(title, string.Empty, objs)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="NativeListItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
/// <param name="subtitle">The subtitle of the Item.</param>
|
||||
/// <param name="objs">The objects that are available on the Item.</param>
|
||||
public NativeListItem(string title, string subtitle, params T[] objs) : base(title, subtitle)
|
||||
{
|
||||
items = new List<T>();
|
||||
items.AddRange(objs);
|
||||
UpdateIndex();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the <seealso cref="ItemChangedEventHandler{T}"/> event.
|
||||
/// </summary>
|
||||
private void TriggerEvent(int index, Direction direction)
|
||||
{
|
||||
ItemChanged?.Invoke(this, new ItemChangedEventArgs<T>(items[index], index, direction));
|
||||
}
|
||||
private void FixIndexIfRequired()
|
||||
{
|
||||
if (index >= items.Count)
|
||||
{
|
||||
index = items.Count - 1;
|
||||
UpdateIndex();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the currently selected item based on the index.
|
||||
/// </summary>
|
||||
private void UpdateIndex()
|
||||
{
|
||||
text.Text = SelectedIndex != -1 ? SelectedItem.ToString() : string.Empty;
|
||||
|
||||
text.Position = new PointF(RightArrow.Position.X - text.Width + 3, text.Position.Y);
|
||||
LeftArrow.Position = new PointF(text.Position.X - LeftArrow.Size.Width, LeftArrow.Position.Y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <typeparamref name="T" /> into this item.
|
||||
/// </summary>
|
||||
/// <param name="item">The <typeparamref name="T" /> to add.</param>
|
||||
public void Add(T item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
if (items.Contains(item))
|
||||
{
|
||||
throw new InvalidOperationException("Item is already part of this NativeListItem.");
|
||||
}
|
||||
|
||||
items.Add(item);
|
||||
|
||||
if (items.Count == 1)
|
||||
{
|
||||
UpdateIndex();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a <typeparamref name="T" /> in a specific location.
|
||||
/// </summary>
|
||||
/// <param name="position">The position where the item should be added.</param>
|
||||
/// <param name="item">The <typeparamref name="T" /> to add.</param>
|
||||
public void Add(int position, T item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
if (position < 0 || position > Items.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(position), "The position is out of the range of items.");
|
||||
}
|
||||
|
||||
Items.Insert(position, item);
|
||||
|
||||
FixIndexIfRequired();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a specific <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <param name="item">The <typeparamref name="T" /> to remove.</param>
|
||||
public void Remove(T item)
|
||||
{
|
||||
if (items.Remove(item))
|
||||
{
|
||||
FixIndexIfRequired();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a <typeparamref name="T" /> at a specific location.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the <typeparamref name="T" />.</param>
|
||||
public void RemoveAt(int position)
|
||||
{
|
||||
if (position >= items.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
items.RemoveAt(position);
|
||||
FixIndexIfRequired();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the items that match the <paramref name="pred"/>.
|
||||
/// </summary>
|
||||
/// <param name="pred">The function to use as a check.</param>
|
||||
public void Remove(Func<T, bool> pred)
|
||||
{
|
||||
if (items.RemoveAll(pred.Invoke) > 0)
|
||||
{
|
||||
FixIndexIfRequired();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the <typeparamref name="T" /> from this item.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
items.Clear();
|
||||
|
||||
UpdateIndex();
|
||||
}
|
||||
/// <summary>
|
||||
/// Recalculates the item positions and sizes with the specified values.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the item.</param>
|
||||
/// <param name="size">The size of the item.</param>
|
||||
/// <param name="selected">If this item has been selected.</param>
|
||||
public override void Recalculate(PointF pos, SizeF size, bool selected)
|
||||
{
|
||||
base.Recalculate(pos, size, selected);
|
||||
|
||||
text.Position = new PointF(pos.X + size.Width - RightArrow.Size.Width - 1 - text.Width, pos.Y + 3);
|
||||
LeftArrow.Position = new PointF(text.Position.X - LeftArrow.Size.Width, pos.Y + 4);
|
||||
}
|
||||
/// <summary>
|
||||
/// Moves to the previous item.
|
||||
/// </summary>
|
||||
public override void GoLeft()
|
||||
{
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
index = Items.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
index--;
|
||||
}
|
||||
|
||||
TriggerEvent(index, Direction.Left);
|
||||
UpdateIndex();
|
||||
}
|
||||
/// <summary>
|
||||
/// Moves to the next item.
|
||||
/// </summary>
|
||||
public override void GoRight()
|
||||
{
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == Items.Count - 1)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
index++;
|
||||
}
|
||||
|
||||
TriggerEvent(index, Direction.Right);
|
||||
UpdateIndex();
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the List on the screen.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw(); // Arrows, Title and Left Badge
|
||||
text.Draw();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
base.UpdateColors();
|
||||
|
||||
if (!Enabled)
|
||||
{
|
||||
text.Color = Colors.TitleDisabled;
|
||||
}
|
||||
else if (lastSelected)
|
||||
{
|
||||
text.Color = Colors.TitleHovered;
|
||||
}
|
||||
else
|
||||
{
|
||||
text.Color = Colors.TitleNormal;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
1689
RageCoop.Client/LemonUI/Menus/NativeMenu.cs
Normal file
1689
RageCoop.Client/LemonUI/Menus/NativeMenu.cs
Normal file
File diff suppressed because it is too large
Load Diff
49
RageCoop.Client/LemonUI/Menus/NativePanel.cs
Normal file
49
RageCoop.Client/LemonUI/Menus/NativePanel.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using LemonUI.Elements;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a panel shown under the description of the item description.
|
||||
/// </summary>
|
||||
public abstract class NativePanel
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// If this panel is visible to the user.
|
||||
/// </summary>
|
||||
public virtual bool Visible { get; set; } = true;
|
||||
/// <summary>
|
||||
/// If the item has controls that can be clicked.
|
||||
/// </summary>
|
||||
public virtual bool Clickable { get; } = false;
|
||||
/// <summary>
|
||||
/// The Background of the panel itself.
|
||||
/// </summary>
|
||||
public ScaledTexture Background { get; } = new ScaledTexture("commonmenu", "gradient_bgd");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the menu contents.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the panel.</param>
|
||||
/// <param name="width">The width of the menu.</param>
|
||||
public virtual void Recalculate(PointF position, float width)
|
||||
{
|
||||
Background.Position = position;
|
||||
}
|
||||
/// <summary>
|
||||
/// Processes and Draws the panel.
|
||||
/// </summary>
|
||||
public virtual void Process()
|
||||
{
|
||||
Background.Draw();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
30
RageCoop.Client/LemonUI/Menus/NativeSeparatorItem.cs
Normal file
30
RageCoop.Client/LemonUI/Menus/NativeSeparatorItem.cs
Normal file
@ -0,0 +1,30 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// A Blank Separator Item for creating empty spaces between menu items.
|
||||
/// </summary>
|
||||
public class NativeSeparatorItem : NativeItem
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Menu Separator.
|
||||
/// </summary>
|
||||
public NativeSeparatorItem() : base(string.Empty, string.Empty, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Draws nothing.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
143
RageCoop.Client/LemonUI/Menus/NativeSlidableItem.cs
Normal file
143
RageCoop.Client/LemonUI/Menus/NativeSlidableItem.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic elements for a slidable item.
|
||||
/// </summary>
|
||||
public abstract class NativeSlidableItem : NativeItem
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private bool alwaysVisible = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Fields
|
||||
|
||||
/// <summary>
|
||||
/// The arrow pointing to the Left.
|
||||
/// </summary>
|
||||
[Obsolete("arrowLeft is Obsolete, use LeftArrow instead.")]
|
||||
internal protected ScaledTexture arrowLeft = null;
|
||||
/// <summary>
|
||||
/// The arrow pointing to the Right.
|
||||
/// </summary>
|
||||
[Obsolete("arrowRight is Obsolete, use RightArrow instead.")]
|
||||
internal protected ScaledTexture arrowRight = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The arrow pointing to the Left.
|
||||
/// </summary>
|
||||
public ScaledTexture LeftArrow { get; }
|
||||
/// <summary>
|
||||
/// The arrow pointing to the Right.
|
||||
/// </summary>
|
||||
public ScaledTexture RightArrow { get; }
|
||||
/// <summary>
|
||||
/// Whether the arrows should always be shown regardless of the visibility of the Item.
|
||||
/// </summary>
|
||||
public bool ArrowsAlwaysVisible
|
||||
{
|
||||
get => alwaysVisible;
|
||||
set
|
||||
{
|
||||
alwaysVisible = value;
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new item that can be sliden.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
/// <param name="description">The description of the Item.</param>
|
||||
public NativeSlidableItem(string title, string description) : base(title, description)
|
||||
{
|
||||
LeftArrow = new ScaledTexture(PointF.Empty, SizeF.Empty, "commonmenu", "arrowleft");
|
||||
RightArrow = new ScaledTexture(PointF.Empty, SizeF.Empty, "commonmenu", "arrowright");
|
||||
#pragma warning disable CS0618
|
||||
arrowLeft = LeftArrow;
|
||||
arrowRight = RightArrow;
|
||||
#pragma warning restore CS0618
|
||||
EnabledChanged += NativeSlidableItem_EnabledChanged;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Local Events
|
||||
|
||||
private void NativeSlidableItem_EnabledChanged(object sender, EventArgs e) => Recalculate();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the item positions and sizes with the specified values.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the item.</param>
|
||||
/// <param name="size">The size of the item.</param>
|
||||
/// <param name="selected">If this item has been selected.</param>
|
||||
public override void Recalculate(PointF pos, SizeF size, bool selected)
|
||||
{
|
||||
base.Recalculate(pos, size, selected);
|
||||
|
||||
LeftArrow.Size = (selected && Enabled) || ArrowsAlwaysVisible ? new SizeF(30, 30) : SizeF.Empty;
|
||||
RightArrow.Size = (selected && Enabled) || ArrowsAlwaysVisible ? new SizeF(30, 30) : SizeF.Empty;
|
||||
|
||||
RightArrow.Position = new PointF(pos.X + size.Width - RightArrow.Size.Width - 5, pos.Y + 4);
|
||||
}
|
||||
/// <summary>
|
||||
/// Moves to the previous item.
|
||||
/// </summary>
|
||||
public abstract void GoLeft();
|
||||
/// <summary>
|
||||
/// Moves to the next item.
|
||||
/// </summary>
|
||||
public abstract void GoRight();
|
||||
/// <summary>
|
||||
/// Draws the left and right arrow.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
title.Draw();
|
||||
badgeLeft?.Draw();
|
||||
LeftArrow.Draw();
|
||||
RightArrow.Draw();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void UpdateColors()
|
||||
{
|
||||
base.UpdateColors();
|
||||
|
||||
if (!Enabled)
|
||||
{
|
||||
LeftArrow.Color = Colors.ArrowsDisabled;
|
||||
RightArrow.Color = Colors.ArrowsDisabled;
|
||||
}
|
||||
else if (lastSelected)
|
||||
{
|
||||
LeftArrow.Color = Colors.ArrowsHovered;
|
||||
RightArrow.Color = Colors.ArrowsHovered;
|
||||
}
|
||||
else
|
||||
{
|
||||
LeftArrow.Color = Colors.ArrowsNormal;
|
||||
RightArrow.Color = Colors.ArrowsNormal;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
241
RageCoop.Client/LemonUI/Menus/NativeSliderItem.cs
Normal file
241
RageCoop.Client/LemonUI/Menus/NativeSliderItem.cs
Normal file
@ -0,0 +1,241 @@
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// A slider item for changing integer values.
|
||||
/// </summary>
|
||||
public class NativeSliderItem : NativeSlidableItem
|
||||
{
|
||||
#region Internal Fields
|
||||
|
||||
/// <summary>
|
||||
/// The background of the slider.
|
||||
/// </summary>
|
||||
internal protected ScaledRectangle background = new ScaledRectangle(PointF.Empty, SizeF.Empty)
|
||||
{
|
||||
Color = Color.FromArgb(255, 4, 32, 57)
|
||||
};
|
||||
/// <summary>
|
||||
/// THe front of the slider.
|
||||
/// </summary>
|
||||
internal protected ScaledRectangle slider = new ScaledRectangle(PointF.Empty, SizeF.Empty)
|
||||
{
|
||||
Color = Color.FromArgb(255, 57, 116, 200)
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// The maximum value of the slider.
|
||||
/// </summary>
|
||||
private int maximum = 0;
|
||||
/// <summary>
|
||||
/// The current value of the slider.
|
||||
/// </summary>
|
||||
private int _value = 100;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The color of the Slider.
|
||||
/// </summary>
|
||||
public Color SliderColor
|
||||
{
|
||||
get => slider.Color;
|
||||
set => slider.Color = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The maximum value of the slider.
|
||||
/// </summary>
|
||||
public int Maximum
|
||||
{
|
||||
get => maximum;
|
||||
set
|
||||
{
|
||||
// If the value was not changed, return
|
||||
if (maximum == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Otherwise, save the new value
|
||||
maximum = value;
|
||||
// If the current value is higher than the max, set the max
|
||||
if (_value > maximum)
|
||||
{
|
||||
_value = maximum;
|
||||
ValueChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
// Finally, update the location of the slider
|
||||
UpdatePosition();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The current value of the slider.
|
||||
/// </summary>
|
||||
public int Value
|
||||
{
|
||||
get => _value;
|
||||
set
|
||||
{
|
||||
// If the value is over the limit, raise an exception
|
||||
if (value > maximum)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), $"The value is over the maximum of {maximum - 1}");
|
||||
}
|
||||
// Otherwise, save it
|
||||
_value = value;
|
||||
// Trigger the respective event
|
||||
ValueChanged?.Invoke(this, EventArgs.Empty);
|
||||
// And update the location of the slider
|
||||
UpdatePosition();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The multiplier for increasing and decreasing the value.
|
||||
/// </summary>
|
||||
public int Multiplier { get; set; } = 1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the value of the menu changes.
|
||||
/// </summary>
|
||||
public event EventHandler ValueChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="NativeSliderItem"/> with a maximum of 100.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
public NativeSliderItem(string title) : this(title, string.Empty, 100, 0)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a <see cref="NativeSliderItem"/> with a maximum of 100.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
/// <param name="description">The description of the Item.</param>
|
||||
public NativeSliderItem(string title, string description) : this(title, description, 100, 0)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a <see cref="NativeSliderItem"/> with a specific current and maximum value.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
/// <param name="max">The maximum value of the Slider.</param>
|
||||
/// <param name="value">The current value of the Slider.</param>
|
||||
public NativeSliderItem(string title, int max, int value) : this(title, string.Empty, max, value)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a <see cref="NativeSliderItem"/> with a specific maximum.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the Item.</param>
|
||||
/// <param name="description">The description of the Item.</param>
|
||||
/// <param name="max">The maximum value of the Slider.</param>
|
||||
/// <param name="value">The current value of the Slider.</param>
|
||||
public NativeSliderItem(string title, string description, int max, int value) : base(title, description)
|
||||
{
|
||||
maximum = max;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Functions
|
||||
|
||||
/// <summary>
|
||||
/// Updates the position of the bar based on the value.
|
||||
/// </summary>
|
||||
internal protected void UpdatePosition()
|
||||
{
|
||||
// Calculate the increment, and then the value of X
|
||||
float increment = _value / (float)maximum;
|
||||
float x = (background.Size.Width - slider.Size.Width) * increment;
|
||||
// Then, add the X to the slider position
|
||||
slider.Position = new PointF(background.Position.X + x, background.Position.Y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the item positions and sizes with the specified values.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position of the item.</param>
|
||||
/// <param name="size">The size of the item.</param>
|
||||
/// <param name="selected">If this item has been selected.</param>
|
||||
public override void Recalculate(PointF pos, SizeF size, bool selected)
|
||||
{
|
||||
base.Recalculate(pos, size, selected);
|
||||
// Set the position and size of the background
|
||||
background.Size = new SizeF(150, 9);
|
||||
background.Position = new PointF(pos.X + size.Width - background.Size.Width - 7 - LeftArrow.Size.Width, pos.Y + 14);
|
||||
// And do the same for the left arrow
|
||||
LeftArrow.Position = new PointF(background.Position.X - LeftArrow.Size.Width, pos.Y + 4);
|
||||
// Finally, set the correct position of the slider
|
||||
slider.Size = new SizeF(75, 9);
|
||||
UpdatePosition();
|
||||
}
|
||||
/// <summary>
|
||||
/// Reduces the value of the slider.
|
||||
/// </summary>
|
||||
public override void GoLeft()
|
||||
{
|
||||
// Calculate the new value
|
||||
int newValue = _value - Multiplier;
|
||||
// If is under zero, set it to zero
|
||||
if (newValue < 0)
|
||||
{
|
||||
Value = 0;
|
||||
}
|
||||
// Otherwise, set it to the new value
|
||||
else
|
||||
{
|
||||
Value = newValue;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Increases the value of the slider.
|
||||
/// </summary>
|
||||
public override void GoRight()
|
||||
{
|
||||
// Calculate the new value
|
||||
int newValue = _value + Multiplier;
|
||||
// If the value is over the maximum, set the max
|
||||
if (newValue > maximum)
|
||||
{
|
||||
Value = maximum;
|
||||
}
|
||||
// Otherwise, set the calculated value
|
||||
else
|
||||
{
|
||||
Value = newValue;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the slider.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
base.Draw(); // Arrows, Title and Left Badge
|
||||
background.Draw();
|
||||
slider.Draw();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
187
RageCoop.Client/LemonUI/Menus/NativeStatsInfo.cs
Normal file
187
RageCoop.Client/LemonUI/Menus/NativeStatsInfo.cs
Normal file
@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using LemonUI.Elements;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Information of a specific field in a <see cref="NativeStatsPanel"/>.
|
||||
/// </summary>
|
||||
public class NativeStatsInfo
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly ScaledText text = new ScaledText(PointF.Empty, string.Empty, 0.35f);
|
||||
private float value = 100;
|
||||
private readonly List<ScaledRectangle> backgrounds = new List<ScaledRectangle>();
|
||||
private readonly List<ScaledRectangle> foregrounds = new List<ScaledRectangle>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The name of the Stats Field.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => text.Text;
|
||||
set => text.Text = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The value of the Stats bar.
|
||||
/// </summary>
|
||||
public float Value
|
||||
{
|
||||
get => value;
|
||||
set
|
||||
{
|
||||
if (value > 100 || value < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "The Value of the Stat can't be over 100 or under 0.");
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
UpdateBars();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Stat Info with the specified name and value set to zero.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Stat.</param>
|
||||
public NativeStatsInfo(string name) : this(name, 0)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new Stat Info with the specified name and value.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the Stat.</param>
|
||||
/// <param name="value"></param>
|
||||
public NativeStatsInfo(string name, int value)
|
||||
{
|
||||
Name = name;
|
||||
this.value = value;
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
backgrounds.Add(new ScaledRectangle(PointF.Empty, SizeF.Empty));
|
||||
foregrounds.Add(new ScaledRectangle(PointF.Empty, SizeF.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
internal void SetColor(Color background, Color foreground)
|
||||
{
|
||||
foreach (ScaledRectangle rectangle in backgrounds)
|
||||
{
|
||||
rectangle.Color = background;
|
||||
}
|
||||
foreach (ScaledRectangle rectangle in foregrounds)
|
||||
{
|
||||
rectangle.Color = foreground;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the values of the bars.
|
||||
/// </summary>
|
||||
private void UpdateBars()
|
||||
{
|
||||
SizeF @default = new SizeF(35, 9);
|
||||
|
||||
// FIRST BAR
|
||||
if (value > 0 && value < 20)
|
||||
{
|
||||
foregrounds[0].Size = new SizeF(@default.Width * (value / 20), @default.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
foregrounds[0].Size = value > 20 ? @default : SizeF.Empty;
|
||||
}
|
||||
|
||||
// SECOND BAR
|
||||
if (value > 20 && value < 40)
|
||||
{
|
||||
foregrounds[1].Size = new SizeF(@default.Width * ((value - 20) / 20), @default.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
foregrounds[1].Size = value > 40 ? @default : SizeF.Empty;
|
||||
}
|
||||
|
||||
// THIRD BAR
|
||||
if (value > 40 && value < 60)
|
||||
{
|
||||
foregrounds[2].Size = new SizeF(@default.Width * ((value - 40) / 20), @default.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
foregrounds[2].Size = value > 60 ? @default : SizeF.Empty;
|
||||
}
|
||||
|
||||
// FOURTH BAR
|
||||
if (value > 60 && value < 80)
|
||||
{
|
||||
foregrounds[3].Size = new SizeF(@default.Width * ((value - 60) / 20), @default.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
foregrounds[3].Size = value > 80 ? @default : SizeF.Empty;
|
||||
}
|
||||
|
||||
// FIFTH BAR
|
||||
if (value > 80 && value < 100)
|
||||
{
|
||||
foregrounds[4].Size = new SizeF(@default.Width * ((value - 80) / 20), @default.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
foregrounds[4].Size = value == 100 ? @default : SizeF.Empty;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Recalculates the position of the stat Text and Bar.
|
||||
/// </summary>
|
||||
/// <param name="position">The new position fot the Stat.</param>
|
||||
/// <param name="width">The Width of the parent Stats Panel.</param>
|
||||
public void Recalculate(PointF position, float width)
|
||||
{
|
||||
text.Position = new PointF(position.X, position.Y);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
PointF pos = new PointF(position.X + width - 234 + ((35 + 3) * i), position.Y + 10);
|
||||
backgrounds[i].Position = pos;
|
||||
backgrounds[i].Size = new SizeF(35, 9);
|
||||
foregrounds[i].Position = pos;
|
||||
}
|
||||
|
||||
UpdateBars();
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the stat information.
|
||||
/// </summary>
|
||||
public void Draw()
|
||||
{
|
||||
foreach (ScaledRectangle background in backgrounds)
|
||||
{
|
||||
background.Draw();
|
||||
}
|
||||
foreach (ScaledRectangle foreground in foregrounds)
|
||||
{
|
||||
foreground.Draw();
|
||||
}
|
||||
text.Draw();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
167
RageCoop.Client/LemonUI/Menus/NativeStatsPanel.cs
Normal file
167
RageCoop.Client/LemonUI/Menus/NativeStatsPanel.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Statistics panel.
|
||||
/// </summary>
|
||||
public class NativeStatsPanel : NativePanel, IContainer<NativeStatsInfo>
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly List<NativeStatsInfo> fields = new List<NativeStatsInfo>();
|
||||
private PointF lastPosition = PointF.Empty;
|
||||
private float lastWidth = 0;
|
||||
private Color backgroundColor = Color.FromArgb(255, 169, 169, 169);
|
||||
private Color foregroundColor = Color.FromArgb(255, 255, 255, 255);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The color of the background of the bars.
|
||||
/// </summary>
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get => backgroundColor;
|
||||
set
|
||||
{
|
||||
backgroundColor = value;
|
||||
|
||||
foreach (NativeStatsInfo field in fields)
|
||||
{
|
||||
field.SetColor(value, foregroundColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The color of the foreground of the bars.
|
||||
/// </summary>
|
||||
public Color ForegroundColor
|
||||
{
|
||||
get => foregroundColor;
|
||||
set
|
||||
{
|
||||
foregroundColor = value;
|
||||
|
||||
foreach (NativeStatsInfo field in fields)
|
||||
{
|
||||
field.SetColor(backgroundColor, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Stats Panel.
|
||||
/// </summary>
|
||||
/// <param name="stats">The Statistics to add.</param>
|
||||
public NativeStatsPanel(params NativeStatsInfo[] stats)
|
||||
{
|
||||
foreach (NativeStatsInfo field in stats)
|
||||
{
|
||||
Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Adds a stat to the player field.
|
||||
/// </summary>
|
||||
/// <param name="field">The Field to add.</param>
|
||||
public void Add(NativeStatsInfo field)
|
||||
{
|
||||
if (fields.Contains(field))
|
||||
{
|
||||
throw new InvalidOperationException("Stat is already part of the panel.");
|
||||
}
|
||||
fields.Add(field);
|
||||
field.SetColor(backgroundColor, foregroundColor);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a field from the panel.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to remove.</param>
|
||||
public void Remove(NativeStatsInfo field)
|
||||
{
|
||||
if (!fields.Contains(field))
|
||||
{
|
||||
return;
|
||||
}
|
||||
fields.Remove(field);
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes the items that match the function.
|
||||
/// </summary>
|
||||
/// <param name="func">The function used to match items.</param>
|
||||
public void Remove(Func<NativeStatsInfo, bool> func)
|
||||
{
|
||||
foreach (NativeStatsInfo item in new List<NativeStatsInfo>(fields))
|
||||
{
|
||||
if (func(item))
|
||||
{
|
||||
Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the Stats fields.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
fields.Clear();
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks if the field is part of the Stats Panel.
|
||||
/// </summary>
|
||||
/// <param name="field">The field to check.</param>
|
||||
/// <returns><see langword="true"/> if the item is part of the Panel, <see langword="false"/> otherwise.</returns>
|
||||
public bool Contains(NativeStatsInfo field) => fields.Contains(field);
|
||||
/// <summary>
|
||||
/// Recalculates the Stats panel with the last known Position and Width.
|
||||
/// </summary>
|
||||
public void Recalculate() => Recalculate(lastPosition, lastWidth);
|
||||
/// <summary>
|
||||
/// Recalculates the position of the Stats panel.
|
||||
/// </summary>
|
||||
/// <param name="position">The new position of the Stats Panel.</param>
|
||||
/// <param name="width">The width of the menu.</param>
|
||||
public override void Recalculate(PointF position, float width)
|
||||
{
|
||||
base.Recalculate(position, width);
|
||||
|
||||
Background.Size = new SizeF(width, (fields.Count * 38) + 9);
|
||||
|
||||
for (int i = 0; i < fields.Count; i++)
|
||||
{
|
||||
fields[i].Recalculate(new PointF(position.X + 9, position.Y + 9 + (38 * i)), width);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Processes the Stats Panel.
|
||||
/// </summary>
|
||||
public override void Process()
|
||||
{
|
||||
base.Process();
|
||||
|
||||
foreach (NativeStatsInfo info in fields)
|
||||
{
|
||||
info.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
75
RageCoop.Client/LemonUI/Menus/NativeSubmenuItem.cs
Normal file
75
RageCoop.Client/LemonUI/Menus/NativeSubmenuItem.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Item used for opening submenus.
|
||||
/// </summary>
|
||||
public class NativeSubmenuItem : NativeItem
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The menu opened by this item.
|
||||
/// </summary>
|
||||
public NativeMenu Menu { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Item that opens a Submenu.
|
||||
/// </summary>
|
||||
/// <param name="menu">The menu that this item will open.</param>
|
||||
/// <param name="parent">The parent menu where this item will be located.</param>
|
||||
public NativeSubmenuItem(NativeMenu menu, NativeMenu parent) : this(menu, parent, ">>>")
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new Item that opens a Submenu.
|
||||
/// </summary>
|
||||
/// <param name="menu">The menu that this item will open.</param>
|
||||
/// <param name="parent">The parent menu where this item will be located.</param>
|
||||
/// <param name="endlabel">The alternative title of the item, shown on the right.</param>
|
||||
public NativeSubmenuItem(NativeMenu menu, NativeMenu parent, string endlabel) : base(menu.Subtitle, menu.Description, endlabel)
|
||||
{
|
||||
Menu = menu ?? throw new ArgumentNullException(nameof(menu));
|
||||
Menu.Parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
|
||||
Activated += NativeSubmenuItem_Activated;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Draw()
|
||||
{
|
||||
// There is no Process(), so let's use draw to update the description
|
||||
if (Description != Menu.Description)
|
||||
{
|
||||
Description = Menu.Description;
|
||||
}
|
||||
|
||||
base.Draw();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Local Events
|
||||
|
||||
private void NativeSubmenuItem_Activated(object sender, EventArgs e)
|
||||
{
|
||||
Menu.Parent.Visible = false;
|
||||
|
||||
if (!Menu.Parent.Visible)
|
||||
{
|
||||
Menu.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
28
RageCoop.Client/LemonUI/Menus/SelectedEventArgs.cs
Normal file
28
RageCoop.Client/LemonUI/Menus/SelectedEventArgs.cs
Normal file
@ -0,0 +1,28 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the selection of an item in the screen.
|
||||
/// </summary>
|
||||
public class SelectedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The index of the item in the full list of items.
|
||||
/// </summary>
|
||||
public int Index { get; }
|
||||
/// <summary>
|
||||
/// The index of the item in the screen.
|
||||
/// </summary>
|
||||
public int OnScreen { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SelectedEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item in the menu.</param>
|
||||
/// <param name="screen">The index of the item based on the number of items shown on screen,</param>
|
||||
public SelectedEventArgs(int index, int screen)
|
||||
{
|
||||
Index = index;
|
||||
OnScreen = screen;
|
||||
}
|
||||
}
|
||||
}
|
9
RageCoop.Client/LemonUI/Menus/SelectedEventHandler.cs
Normal file
9
RageCoop.Client/LemonUI/Menus/SelectedEventHandler.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the method that is called when a new item is selected in the Menu.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">A <see cref="SelectedEventArgs"/> with the index information.</param>
|
||||
public delegate void SelectedEventHandler(object sender, SelectedEventArgs e);
|
||||
}
|
21
RageCoop.Client/LemonUI/Menus/SubtitleBehavior.cs
Normal file
21
RageCoop.Client/LemonUI/Menus/SubtitleBehavior.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace LemonUI.Menus
|
||||
{
|
||||
/// <summary>
|
||||
/// The behavior of the <see cref="NativeMenu"/>'s subtitle.
|
||||
/// </summary>
|
||||
public enum SubtitleBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// The subtitle will always be shown.
|
||||
/// </summary>
|
||||
AlwaysShow = 0,
|
||||
/// <summary>
|
||||
/// The subtitle will always be shown, except when is empty.
|
||||
/// </summary>
|
||||
ShowIfRequired = 1,
|
||||
/// <summary>
|
||||
/// The subtitle will never be shown.
|
||||
/// </summary>
|
||||
AlwaysHide = 2
|
||||
}
|
||||
}
|
297
RageCoop.Client/LemonUI/ObjectPool.cs
Normal file
297
RageCoop.Client/LemonUI/ObjectPool.cs
Normal file
@ -0,0 +1,297 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.UI;
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
using RAGE.NUI;
|
||||
#elif RPH
|
||||
using Rage;
|
||||
using Rage.Native;
|
||||
#elif SHVDN3
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the method that reports a Resolution change in the Game Settings.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">A <see cref="ResolutionChangedEventArgs"/> containing the Previous and Current resolution.</param>
|
||||
public delegate void ResolutionChangedEventHandler(object sender, ResolutionChangedEventArgs e);
|
||||
/// <summary>
|
||||
/// Represents the method that reports a Safe Zone change in the Game Settings.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event event.</param>
|
||||
/// <param name="e">A <see cref="ResolutionChangedEventArgs"/> containing the Previous and Current Safe Zone.</param>
|
||||
public delegate void SafeZoneChangedEventHandler(object sender, SafeZoneChangedEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the information after a Resolution Change in the game.
|
||||
/// </summary>
|
||||
public class ResolutionChangedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The Game Resolution before it was changed.
|
||||
/// </summary>
|
||||
public SizeF Before { get; }
|
||||
/// <summary>
|
||||
/// The Game Resolution after it was changed.
|
||||
/// </summary>
|
||||
public SizeF After { get; }
|
||||
|
||||
internal ResolutionChangedEventArgs(SizeF before, SizeF after)
|
||||
{
|
||||
Before = before;
|
||||
After = after;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Represents the information after a Safe Zone Change in the game.
|
||||
/// </summary>
|
||||
public class SafeZoneChangedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The raw Safezone size before the change.
|
||||
/// </summary>
|
||||
public float Before { get; }
|
||||
/// <summary>
|
||||
/// The Safezone size after the change.
|
||||
/// </summary>
|
||||
public float After { get; }
|
||||
|
||||
internal SafeZoneChangedEventArgs(float before, float after)
|
||||
{
|
||||
Before = before;
|
||||
After = after;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manager for Menus and Items.
|
||||
/// </summary>
|
||||
public class ObjectPool
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// The last known resolution by the object pool.
|
||||
/// </summary>
|
||||
#if FIVEM
|
||||
private SizeF lastKnownResolution = CitizenFX.Core.UI.Screen.Resolution;
|
||||
#elif RAGEMP
|
||||
private SizeF lastKnownResolution = new SizeF(Game.ScreenResolution.Width, Game.ScreenResolution.Height);
|
||||
#elif RPH
|
||||
private SizeF lastKnownResolution = Game.Resolution;
|
||||
#elif SHVDN3
|
||||
private SizeF lastKnownResolution = GTA.UI.Screen.Resolution;
|
||||
#endif
|
||||
/// <summary>
|
||||
/// The last know Safezone size.
|
||||
/// </summary>
|
||||
#if FIVEM
|
||||
private float lastKnownSafezone = API.GetSafeZoneSize();
|
||||
#elif RAGEMP
|
||||
private float lastKnownSafezone = Invoker.Invoke<float>(Natives.GetSafeZoneSize);
|
||||
#elif RPH
|
||||
private float lastKnownSafezone = NativeFunction.CallByHash<float>(0xBAF107B6BB2C97F0);
|
||||
#elif SHVDN3
|
||||
private float lastKnownSafezone = Function.Call<float>(Hash.GET_SAFE_ZONE_SIZE);
|
||||
#endif
|
||||
/// <summary>
|
||||
/// The list of processable objects.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<int, IProcessable> objects = new ConcurrentDictionary<int, IProcessable>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Checks if there are objects visible on the screen.
|
||||
/// </summary>
|
||||
public bool AreAnyVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
// Iterate over the objects
|
||||
foreach (IProcessable obj in objects.Values)
|
||||
{
|
||||
// If is visible return true
|
||||
if (obj.Visible)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If none were visible return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the game resolution is changed.
|
||||
/// </summary>
|
||||
public event ResolutionChangedEventHandler ResolutionChanged;
|
||||
/// <summary>
|
||||
/// Event triggered when the Safezone size option in the Display settings is changed.
|
||||
/// </summary>
|
||||
public event SafeZoneChangedEventHandler SafezoneChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
/// <summary>
|
||||
/// Detects resolution changes by comparing the last known resolution and the current one.
|
||||
/// </summary>
|
||||
private void DetectResolutionChanges()
|
||||
{
|
||||
// Get the current resolution
|
||||
#if FIVEM
|
||||
SizeF resolution = CitizenFX.Core.UI.Screen.Resolution;
|
||||
#elif RAGEMP
|
||||
ScreenResolutionType raw = Game.ScreenResolution;
|
||||
SizeF resolution = new SizeF(raw.Width, raw.Height);
|
||||
#elif RPH
|
||||
SizeF resolution = Game.Resolution;
|
||||
#elif SHVDN3
|
||||
SizeF resolution = GTA.UI.Screen.Resolution;
|
||||
#endif
|
||||
// If the old res does not matches the current one
|
||||
if (lastKnownResolution != resolution)
|
||||
{
|
||||
// Trigger the event
|
||||
ResolutionChanged?.Invoke(this, new ResolutionChangedEventArgs(lastKnownResolution, resolution));
|
||||
// Refresh everything
|
||||
RefreshAll();
|
||||
// And save the new resolution
|
||||
lastKnownResolution = resolution;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Detects Safezone changes by comparing the last known value to the current one.
|
||||
/// </summary>
|
||||
private void DetectSafezoneChanges()
|
||||
{
|
||||
// Get the current Safezone size
|
||||
#if FIVEM
|
||||
float safezone = API.GetSafeZoneSize();
|
||||
#elif RAGEMP
|
||||
float safezone = Invoker.Invoke<float>(Natives.GetSafeZoneSize);
|
||||
#elif RPH
|
||||
float safezone = NativeFunction.CallByHash<float>(0xBAF107B6BB2C97F0);
|
||||
#elif SHVDN3
|
||||
float safezone = Function.Call<float>(Hash.GET_SAFE_ZONE_SIZE);
|
||||
#endif
|
||||
|
||||
// If is not the same as the last one
|
||||
if (lastKnownSafezone != safezone)
|
||||
{
|
||||
// Trigger the event
|
||||
SafezoneChanged?.Invoke(this, new SafeZoneChangedEventArgs(lastKnownSafezone, safezone));
|
||||
// Refresh everything
|
||||
RefreshAll();
|
||||
// And save the new safezone
|
||||
lastKnownSafezone = safezone;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Function
|
||||
|
||||
/// <summary>
|
||||
/// Adds the object into the pool.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to add.</param>
|
||||
public void Add(IProcessable obj)
|
||||
{
|
||||
// Make sure that the object is not null
|
||||
if (obj == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(obj));
|
||||
}
|
||||
|
||||
int key = obj.GetHashCode();
|
||||
// Otherwise, add it to the general pool
|
||||
if (objects.ContainsKey(key))
|
||||
{
|
||||
throw new InvalidOperationException("The object is already part of this pool.");
|
||||
}
|
||||
objects.TryAdd(key, obj);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes the object from the pool.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to remove.</param>
|
||||
public void Remove(IProcessable obj)
|
||||
{
|
||||
objects.TryRemove(obj.GetHashCode(), out _);
|
||||
}
|
||||
/// <summary>
|
||||
/// Performs the specified action on each element that matches T.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to match.</typeparam>
|
||||
/// <param name="action">The action delegate to perform on each T.</param>
|
||||
public void ForEach<T>(Action<T> action)
|
||||
{
|
||||
foreach (IProcessable obj in objects.Values)
|
||||
{
|
||||
if (obj is T conv)
|
||||
{
|
||||
action(conv);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Refreshes all of the items.
|
||||
/// </summary>
|
||||
public void RefreshAll()
|
||||
{
|
||||
// Iterate over the objects and recalculate those possible
|
||||
foreach (IProcessable obj in objects.Values)
|
||||
{
|
||||
if (obj is IRecalculable recal)
|
||||
{
|
||||
recal.Recalculate();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Hides all of the objects.
|
||||
/// </summary>
|
||||
public void HideAll()
|
||||
{
|
||||
foreach (IProcessable obj in objects.Values)
|
||||
{
|
||||
obj.Visible = false;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Processes the objects and features in this pool.
|
||||
/// This needs to be called every tick.
|
||||
/// </summary>
|
||||
public void Process()
|
||||
{
|
||||
// See if there are resolution or safezone changes
|
||||
DetectResolutionChanges();
|
||||
DetectSafezoneChanges();
|
||||
// And process the objects in the pool
|
||||
foreach (IProcessable obj in objects.Values)
|
||||
{
|
||||
obj.Process();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
342
RageCoop.Client/LemonUI/Scaleform/BaseScaleform.cs
Normal file
342
RageCoop.Client/LemonUI/Scaleform/BaseScaleform.cs
Normal file
@ -0,0 +1,342 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage.Native;
|
||||
#elif SHVDN3
|
||||
using GTA.Native;
|
||||
#endif
|
||||
using System;
|
||||
|
||||
namespace LemonUI.Scaleform
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic Scaleform object.
|
||||
/// </summary>
|
||||
public abstract class BaseScaleform : IScaleform
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
#if FIVEM || SHVDN3
|
||||
/// <summary>
|
||||
/// The ID of the scaleform.
|
||||
/// </summary>
|
||||
[Obsolete("Please use the Handle or Name properties and call the methods manually.", true)]
|
||||
#endif
|
||||
#if FIVEM
|
||||
protected CitizenFX.Core.Scaleform scaleform = new CitizenFX.Core.Scaleform(string.Empty);
|
||||
#elif SHVDN3
|
||||
protected GTA.Scaleform scaleform = new GTA.Scaleform(string.Empty);
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The ID or Handle of the Scaleform.
|
||||
/// </summary>
|
||||
public int Handle { get; private set; }
|
||||
/// <summary>
|
||||
/// The Name of the Scaleform.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// If the Scaleform should be visible or not.
|
||||
/// </summary>
|
||||
public bool Visible { get; set; }
|
||||
/// <summary>
|
||||
/// If the Scaleform is loaded or not.
|
||||
/// </summary>
|
||||
public bool IsLoaded
|
||||
{
|
||||
get
|
||||
{
|
||||
#if FIVEM
|
||||
return API.HasScaleformMovieLoaded(Handle);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<bool>(Natives.HasScaleformMovieLoaded, Handle);
|
||||
#elif RPH
|
||||
return NativeFunction.CallByHash<bool>(0x85F01B8D5B90570E, Handle);
|
||||
#elif SHVDN3
|
||||
return Function.Call<bool>(Hash.HAS_SCALEFORM_MOVIE_LOADED, Handle);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Scaleform class with the specified Scaleform object name.
|
||||
/// </summary>
|
||||
/// <param name="sc">The Scalform object.</param>
|
||||
public BaseScaleform(string sc)
|
||||
{
|
||||
Name = sc;
|
||||
|
||||
#if FIVEM
|
||||
Handle = API.RequestScaleformMovie(Name);
|
||||
#elif RAGEMP
|
||||
Handle = Invoker.Invoke<int>(Natives.RequestScaleformMovie, Name);
|
||||
#elif RPH
|
||||
Handle = NativeFunction.CallByHash<int>(0x11FE353CF9733E6F, Name);
|
||||
#elif SHVDN3
|
||||
Handle = Function.Call<int>(Hash.REQUEST_SCALEFORM_MOVIE, Name);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Functions
|
||||
|
||||
private void CallFunctionBase(string function, params object[] parameters)
|
||||
{
|
||||
#if FIVEM
|
||||
API.BeginScaleformMovieMethod(Handle, function);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xF6E48914C7A8694E, Handle, function);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xF6E48914C7A8694E, Handle, function);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0xF6E48914C7A8694E, Handle, function);
|
||||
#endif
|
||||
|
||||
foreach (object obj in parameters)
|
||||
{
|
||||
if (obj is int objInt)
|
||||
{
|
||||
#if FIVEM
|
||||
API.ScaleformMovieMethodAddParamInt(objInt);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xC3D0841A0CC546A6, objInt);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xC3D0841A0CC546A6, objInt);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0xC3D0841A0CC546A6, objInt);
|
||||
#endif
|
||||
}
|
||||
else if (obj is string objString)
|
||||
{
|
||||
#if FIVEM
|
||||
API.BeginTextCommandScaleformString("STRING");
|
||||
API.AddTextComponentSubstringPlayerName(objString);
|
||||
API.EndTextCommandScaleformString();
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.BeginTextCommandScaleformString, "STRING");
|
||||
Invoker.Invoke(Natives.AddTextComponentSubstringPlayerName, objString);
|
||||
Invoker.Invoke(Natives.EndTextCommandScaleformString);
|
||||
#elif RPH
|
||||
|
||||
NativeFunction.CallByHash<int>(0x80338406F3475E55, "STRING");
|
||||
NativeFunction.CallByHash<int>(0x6C188BE134E074AA, objString);
|
||||
NativeFunction.CallByHash<int>(0x362E2D3FE93A9959);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0x80338406F3475E55, "STRING");
|
||||
Function.Call((Hash)0x6C188BE134E074AA, objString);
|
||||
Function.Call((Hash)0x362E2D3FE93A9959);
|
||||
#endif
|
||||
}
|
||||
else if (obj is float objFloat)
|
||||
{
|
||||
#if FIVEM
|
||||
API.ScaleformMovieMethodAddParamFloat(objFloat);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xD69736AAE04DB51A, objFloat);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xD69736AAE04DB51A, objFloat);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0xD69736AAE04DB51A, objFloat);
|
||||
#endif
|
||||
}
|
||||
else if (obj is double objDouble)
|
||||
{
|
||||
#if FIVEM
|
||||
API.ScaleformMovieMethodAddParamFloat((float)objDouble);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xD69736AAE04DB51A, (float)objDouble);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xD69736AAE04DB51A, (float)objDouble);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0xD69736AAE04DB51A, (float)objDouble);
|
||||
#endif
|
||||
}
|
||||
else if (obj is bool objBool)
|
||||
{
|
||||
#if FIVEM
|
||||
API.ScaleformMovieMethodAddParamBool(objBool);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xC58424BA936EB458, objBool);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xC58424BA936EB458, objBool);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0xC58424BA936EB458, objBool);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Unexpected argument type {obj.GetType().Name}.", nameof(parameters));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified Scaleform Return Value is ready to be fetched.
|
||||
/// </summary>
|
||||
/// <param name="id">The Identifier of the Value.</param>
|
||||
/// <returns><see langword="true"/> if the value is ready, <see langword="false"/> otherwise.</returns>
|
||||
public bool IsValueReady(int id)
|
||||
{
|
||||
#if FIVEM
|
||||
return API.IsScaleformMovieMethodReturnValueReady(id);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<bool>(Natives._0x768FF8961BA904D6, id);
|
||||
#elif RPH
|
||||
return NativeFunction.CallByHash<bool>(0x768FF8961BA904D6, id);
|
||||
#elif SHVDN3
|
||||
return Function.Call<bool>(Hash.IS_SCALEFORM_MOVIE_METHOD_RETURN_VALUE_READY, id);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a specific value.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of value.</typeparam>
|
||||
/// <param name="id">The Identifier of the value.</param>
|
||||
/// <returns>The value returned by the native.</returns>
|
||||
public T GetValue<T>(int id)
|
||||
{
|
||||
if (typeof(T) == typeof(string))
|
||||
{
|
||||
#if FIVEM
|
||||
return (T)(object)API.GetScaleformMovieMethodReturnValueString(id);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<T>(0xE1E258829A885245, id);
|
||||
#elif RPH
|
||||
return (T)NativeFunction.CallByHash(0xE1E258829A885245, typeof(string), id);
|
||||
#elif SHVDN3
|
||||
return (T)(object)Function.Call<string>(Hash.GET_SCALEFORM_MOVIE_METHOD_RETURN_VALUE_STRING, id);
|
||||
#endif
|
||||
}
|
||||
else if (typeof(T) == typeof(int))
|
||||
{
|
||||
#if FIVEM
|
||||
return (T)(object)API.GetScaleformMovieMethodReturnValueInt(id);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<T>(0x2DE7EFA66B906036, id);
|
||||
#elif RPH
|
||||
return (T)(object)NativeFunction.CallByHash<int>(0x2DE7EFA66B906036, id);
|
||||
#elif SHVDN3
|
||||
return (T)(object)Function.Call<int>(Hash.GET_SCALEFORM_MOVIE_METHOD_RETURN_VALUE_INT, id);
|
||||
#endif
|
||||
}
|
||||
else if (typeof(T) == typeof(bool))
|
||||
{
|
||||
#if FIVEM
|
||||
return (T)(object)API.GetScaleformMovieMethodReturnValueBool(id);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<T>(0xD80A80346A45D761, id);
|
||||
#elif RPH
|
||||
return (T)(object)NativeFunction.CallByHash<bool>(0xD80A80346A45D761, id);
|
||||
#elif SHVDN3
|
||||
return (T)(object)Function.Call<bool>(Hash._GET_SCALEFORM_MOVIE_METHOD_RETURN_VALUE_BOOL, id);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Expected string, int or bool, got {typeof(T).Name}.");
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Calls a Scaleform function.
|
||||
/// </summary>
|
||||
/// <param name="function">The name of the function to call.</param>
|
||||
/// <param name="parameters">The parameters to pass.</param>
|
||||
public void CallFunction(string function, params object[] parameters)
|
||||
{
|
||||
CallFunctionBase(function, parameters);
|
||||
#if FIVEM
|
||||
API.EndScaleformMovieMethod();
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xC6796A8FFA375E53);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xC6796A8FFA375E53);
|
||||
#elif SHVDN3
|
||||
Function.Call((Hash)0xC6796A8FFA375E53);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Calls a Scaleform function with a return value.
|
||||
/// </summary>
|
||||
/// <param name="function">The name of the function to call.</param>
|
||||
/// <param name="parameters">The parameters to pass.</param>
|
||||
public int CallFunctionReturn(string function, params object[] parameters)
|
||||
{
|
||||
CallFunctionBase(function, parameters);
|
||||
#if FIVEM
|
||||
return API.EndScaleformMovieMethodReturnValue();
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<int>(0xC50AA39A577AF886);
|
||||
#elif RPH
|
||||
return NativeFunction.CallByHash<int>(0xC50AA39A577AF886);
|
||||
#elif SHVDN3
|
||||
return Function.Call<int>((Hash)0xC50AA39A577AF886);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the parameters of the Scaleform.
|
||||
/// </summary>
|
||||
public abstract void Update();
|
||||
/// <summary>
|
||||
/// Draws the scaleform full screen.
|
||||
/// </summary>
|
||||
public virtual void DrawFullScreen()
|
||||
{
|
||||
if (!Visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Update();
|
||||
#if FIVEM
|
||||
API.DrawScaleformMovieFullscreen(Handle, 255, 255, 255, 255, 0);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.DrawScaleformMovieFullscreen, 255, 255, 255, 255, 0);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x0DF606929C105BE1, Handle, 255, 255, 255, 255, 0);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.DRAW_SCALEFORM_MOVIE_FULLSCREEN, Handle, 255, 255, 255, 255, 0);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the scaleform full screen.
|
||||
/// </summary>
|
||||
public virtual void Draw() => DrawFullScreen();
|
||||
/// <summary>
|
||||
/// Draws the scaleform full screen.
|
||||
/// </summary>
|
||||
public virtual void Process() => DrawFullScreen();
|
||||
/// <summary>
|
||||
/// Marks the scaleform as no longer needed.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
int id = Handle;
|
||||
#if FIVEM
|
||||
API.SetScaleformMovieAsNoLongerNeeded(ref id);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.SetScaleformMovieAsNoLongerNeeded, id);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x1D132D614DD86811, new NativeArgument(id));
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.SET_SCALEFORM_MOVIE_AS_NO_LONGER_NEEDED, new OutputArgument(id));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
357
RageCoop.Client/LemonUI/Scaleform/BigMessage.cs
Normal file
357
RageCoop.Client/LemonUI/Scaleform/BigMessage.cs
Normal file
@ -0,0 +1,357 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
#endif
|
||||
using System;
|
||||
|
||||
namespace LemonUI.Scaleform
|
||||
{
|
||||
/// <summary>
|
||||
/// The type for a big message.
|
||||
/// </summary>
|
||||
public enum MessageType
|
||||
{
|
||||
/// <summary>
|
||||
/// A centered message with customizable text an d background colors.
|
||||
/// Internally called SHOW_SHARD_CENTERED_MP_MESSAGE.
|
||||
/// </summary>
|
||||
Customizable = 0,
|
||||
/// <summary>
|
||||
/// Used when you rank up on GTA Online.
|
||||
/// Internally called SHOW_SHARD_CREW_RANKUP_MP_MESSAGE.
|
||||
/// </summary>
|
||||
RankUp = 1,
|
||||
/// <summary>
|
||||
/// The Mission Passed screen on PS3 and Xbox 360.
|
||||
/// Internally called SHOW_MISSION_PASSED_MESSAGE.
|
||||
/// </summary>
|
||||
MissionPassedOldGen = 2,
|
||||
/// <summary>
|
||||
/// The Message Type shown on the Wasted screen.
|
||||
/// Internally called SHOW_SHARD_WASTED_MP_MESSAGE.
|
||||
/// </summary>
|
||||
Wasted = 3,
|
||||
/// <summary>
|
||||
/// Used on the GTA Online Freemode event announcements.
|
||||
/// Internally called SHOW_PLANE_MESSAGE.
|
||||
/// </summary>
|
||||
Plane = 4,
|
||||
/// <summary>
|
||||
/// Development leftover from when GTA Online was Cops and Crooks.
|
||||
/// Internally called SHOW_BIG_MP_MESSAGE.
|
||||
/// </summary>
|
||||
CopsAndCrooks = 5,
|
||||
/// <summary>
|
||||
/// Message shown when the player purchases a weapon.
|
||||
/// Internally called SHOW_WEAPON_PURCHASED.
|
||||
/// </summary>
|
||||
Weapon = 6,
|
||||
/// <summary>
|
||||
/// Unknown where this one is used.
|
||||
/// Internally called SHOW_CENTERED_MP_MESSAGE_LARGE.
|
||||
/// </summary>
|
||||
CenteredLarge = 7,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A customizable big message.
|
||||
/// </summary>
|
||||
public class BigMessage : BaseScaleform
|
||||
{
|
||||
#region Constant Fields
|
||||
|
||||
private const uint unarmed = 0xA2719263;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private MessageType type;
|
||||
private uint weaponHash;
|
||||
private uint hideAfter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The title of the message.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
/// <summary>
|
||||
/// The subtitle or description of the message.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
/// <summary>
|
||||
/// The color of the text.
|
||||
/// Only used on the Customizable message type.
|
||||
/// </summary>
|
||||
public int TextColor { get; set; }
|
||||
/// <summary>
|
||||
/// The color of the background in the Customizable message type.
|
||||
/// </summary>
|
||||
public int BackgroundColor { get; set; }
|
||||
/// <summary>
|
||||
/// The Rank when the mode is set to Cops and Crooks.
|
||||
/// </summary>
|
||||
public string Rank { get; set; }
|
||||
#if !RAGEMP
|
||||
/// <summary>
|
||||
/// The hash of the Weapon as an enum.
|
||||
/// </summary>
|
||||
public WeaponHash Weapon
|
||||
{
|
||||
get => (WeaponHash)weaponHash;
|
||||
set => weaponHash = (uint)value;
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// The hash of the Weapon as it's native value.
|
||||
/// </summary>
|
||||
public uint WeaponHash
|
||||
{
|
||||
get => weaponHash;
|
||||
set => weaponHash = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The type of message to show.
|
||||
/// </summary>
|
||||
public MessageType Type
|
||||
{
|
||||
get => type;
|
||||
set
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(MessageType), value))
|
||||
{
|
||||
throw new InvalidOperationException($"{value} is not a valid message type.");
|
||||
}
|
||||
type = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a standard customizable message with just a title.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
public BigMessage(string title) : this(title, string.Empty, string.Empty, unarmed, 0, 0, MessageType.Customizable)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a custom message with the specified title.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="type">The type of message.</param>
|
||||
public BigMessage(string title, MessageType type) : this(title, string.Empty, string.Empty, unarmed, 0, 0, type)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a standard customizable message with a title and message.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="message">The message to show.</param>
|
||||
public BigMessage(string title, string message) : this(title, message, string.Empty, unarmed, 0, 0, MessageType.Customizable)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a Cops and Crooks message type.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="message">The message to show.</param>
|
||||
/// <param name="rank">Text to show in the Rank space.</param>
|
||||
public BigMessage(string title, string message, string rank) : this(title, message, rank, unarmed, 0, 0, MessageType.CopsAndCrooks)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a message with the specified type, title and message.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="message">The message to show.</param>
|
||||
/// <param name="type">The type of message.</param>
|
||||
public BigMessage(string title, string message, MessageType type) : this(title, message, string.Empty, unarmed, 0, 0, type)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a standard customizable message with a title and a custom text color.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="colorText">The color of the text.</param>
|
||||
public BigMessage(string title, int colorText) : this(title, string.Empty, string.Empty, unarmed, colorText, 0, MessageType.Customizable)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a standard customizable message with a specific title and custom colors.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="colorText">The color of the text.</param>
|
||||
/// <param name="colorBackground">The color of the background.</param>
|
||||
public BigMessage(string title, int colorText, int colorBackground) : this(title, string.Empty, string.Empty, unarmed, colorText, colorBackground, MessageType.Customizable)
|
||||
{
|
||||
}
|
||||
#if !RAGEMP
|
||||
/// <summary>
|
||||
/// Creates a Weapon Purchase message with a custom text and weapons.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="weapon">The name of the Weapon.</param>
|
||||
/// <param name="hash">The hash of the Weapon image.</param>
|
||||
public BigMessage(string title, string weapon, WeaponHash hash) : this(title, string.Empty, weapon, hash, 0, 0, MessageType.Weapon)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a Weapon Purchase message with a custom text and weapons.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="message">The message to show.</param>
|
||||
/// <param name="weapon">The name of the Weapon.</param>
|
||||
/// <param name="hash">The hash of the Weapon image.</param>
|
||||
public BigMessage(string title, string message, string weapon, WeaponHash hash) : this(title, message, weapon, hash, 0, 0, MessageType.Weapon)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a message with all of the selected information.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="message">The message to show.</param>
|
||||
/// <param name="rank">The Rank on Cops and Crooks.</param>
|
||||
/// <param name="weapon">The hash of the Weapon image.</param>
|
||||
/// <param name="colorText">The color of the text.</param>
|
||||
/// <param name="colorBackground">The color of the background.</param>
|
||||
/// <param name="type">The type of message.</param>
|
||||
public BigMessage(string title, string message, string rank, WeaponHash weapon, int colorText, int colorBackground, MessageType type) : this(title, message, rank, (uint)weapon, colorText, colorBackground, type)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Creates a message with all of the selected information.
|
||||
/// </summary>
|
||||
/// <param name="title">The title to use.</param>
|
||||
/// <param name="message">The message to show.</param>
|
||||
/// <param name="rank">The Rank on Cops and Crooks.</param>
|
||||
/// <param name="weapon">The hash of the Weapon image.</param>
|
||||
/// <param name="colorText">The color of the text.</param>
|
||||
/// <param name="colorBackground">The color of the background.</param>
|
||||
/// <param name="type">The type of message.</param>
|
||||
public BigMessage(string title, string message, string rank, uint weapon, int colorText, int colorBackground, MessageType type) : base("MP_BIG_MESSAGE_FREEMODE")
|
||||
{
|
||||
Title = title;
|
||||
Message = message;
|
||||
Rank = rank;
|
||||
WeaponHash = weapon;
|
||||
TextColor = colorText;
|
||||
BackgroundColor = colorBackground;
|
||||
Type = type;
|
||||
Update();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Message information in the Scaleform.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
// Select the correct function to call
|
||||
string function;
|
||||
switch (type)
|
||||
{
|
||||
case MessageType.Customizable:
|
||||
function = "SHOW_SHARD_CENTERED_MP_MESSAGE";
|
||||
break;
|
||||
case MessageType.RankUp:
|
||||
function = "SHOW_SHARD_CREW_RANKUP_MP_MESSAGE";
|
||||
break;
|
||||
case MessageType.MissionPassedOldGen:
|
||||
function = "SHOW_MISSION_PASSED_MESSAGE";
|
||||
break;
|
||||
case MessageType.Wasted:
|
||||
function = "SHOW_SHARD_WASTED_MP_MESSAGE";
|
||||
break;
|
||||
case MessageType.Plane:
|
||||
function = "SHOW_PLANE_MESSAGE";
|
||||
break;
|
||||
case MessageType.CopsAndCrooks:
|
||||
function = "SHOW_BIG_MP_MESSAGE";
|
||||
break;
|
||||
case MessageType.Weapon:
|
||||
function = "SHOW_WEAPON_PURCHASED";
|
||||
break;
|
||||
case MessageType.CenteredLarge:
|
||||
function = "SHOW_CENTERED_MP_MESSAGE_LARGE";
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"{type} is not a valid message type.");
|
||||
}
|
||||
|
||||
// And add the parameters
|
||||
switch (type)
|
||||
{
|
||||
case MessageType.Customizable:
|
||||
CallFunction(function, Title, Message, TextColor, BackgroundColor);
|
||||
break;
|
||||
case MessageType.CopsAndCrooks:
|
||||
CallFunction(function, Title, Message, Rank);
|
||||
break;
|
||||
case MessageType.Weapon:
|
||||
CallFunction(function, Title, Message, (int)weaponHash);
|
||||
break;
|
||||
default:
|
||||
CallFunction(function, Title, Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Fades the big message out.
|
||||
/// </summary>
|
||||
/// <param name="time">The time it will take to do the fade.</param>
|
||||
public void FadeOut(int time)
|
||||
{
|
||||
if (time < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(time), "Time can't be under zero.");
|
||||
}
|
||||
|
||||
CallFunction("SHARD_ANIM_OUT", 0, time);
|
||||
|
||||
#if RAGEMP
|
||||
uint currentTime = (uint)Misc.GetGameTimer();
|
||||
#elif RPH
|
||||
uint currentTime = Game.GameTime;
|
||||
#else
|
||||
uint currentTime = (uint)Game.GameTime;
|
||||
#endif
|
||||
hideAfter = currentTime + (uint)time;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void DrawFullScreen()
|
||||
{
|
||||
#if RAGEMP
|
||||
uint time = (uint)Misc.GetGameTimer();
|
||||
#elif RPH
|
||||
uint time = Game.GameTime;
|
||||
#else
|
||||
uint time = (uint)Game.GameTime;
|
||||
#endif
|
||||
|
||||
if (hideAfter > 0 && time > hideAfter)
|
||||
{
|
||||
Visible = false;
|
||||
hideAfter = 0;
|
||||
}
|
||||
|
||||
base.DrawFullScreen();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
425
RageCoop.Client/LemonUI/Scaleform/BruteForce.cs
Normal file
425
RageCoop.Client/LemonUI/Scaleform/BruteForce.cs
Normal file
@ -0,0 +1,425 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage;
|
||||
using Rage.Native;
|
||||
using Control = Rage.GameControl;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LemonUI.Scaleform
|
||||
{
|
||||
/// <summary>
|
||||
/// The Background of the BruteForce Hack Minigame.
|
||||
/// </summary>
|
||||
public enum BruteForceBackground
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple Black background.
|
||||
/// </summary>
|
||||
Black = 0,
|
||||
/// <summary>
|
||||
/// A simple Purple background.
|
||||
/// </summary>
|
||||
Purple = 1,
|
||||
/// <summary>
|
||||
/// A simple Gray background.
|
||||
/// </summary>
|
||||
Gray = 2,
|
||||
/// <summary>
|
||||
/// A simple Light Blue background.
|
||||
/// </summary>
|
||||
LightBlue = 3,
|
||||
/// <summary>
|
||||
/// A Light Blue Wallpaper.
|
||||
/// </summary>
|
||||
Wallpaper1 = 4,
|
||||
/// <summary>
|
||||
/// A Fade from Gray in the center to Black in the corners.
|
||||
/// </summary>
|
||||
DarkFade = 5,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The status of the BruteForce Hack after finishing.
|
||||
/// </summary>
|
||||
public enum BruteForceStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The user completed the hack successfully.
|
||||
/// </summary>
|
||||
Completed = 0,
|
||||
/// <summary>
|
||||
/// The user ran out of time.
|
||||
/// </summary>
|
||||
OutOfTime = 1,
|
||||
/// <summary>
|
||||
/// The player ran out of lives.
|
||||
/// </summary>
|
||||
OutOfLives = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event information after an the BruteForce hack has finished.
|
||||
/// </summary>
|
||||
public class BruteForceFinishedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The final status of the Hack.
|
||||
/// </summary>
|
||||
public BruteForceStatus Status { get; }
|
||||
|
||||
internal BruteForceFinishedEventArgs(BruteForceStatus status)
|
||||
{
|
||||
Status = status;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the method that is called when the end user finishes the BruteForce hack.
|
||||
/// </summary>
|
||||
/// <param name="sender">The source of the event.</param>
|
||||
/// <param name="e">An <see cref="BruteForceFinishedEventArgs"/> with the hack status.</param>
|
||||
public delegate void BruteForceFinishedEventHandler(object sender, BruteForceFinishedEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// The BruteForce Hacking Minigame shown in multiple missions.
|
||||
/// </summary>
|
||||
public class BruteForce : BaseScaleform
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private static readonly Random random = new Random();
|
||||
private static readonly Sound soundRowSwitch = new Sound(string.Empty, "HACKING_MOVE_CURSOR");
|
||||
private static readonly Sound soundRowCompleted = new Sound(string.Empty, "HACKING_CLICK");
|
||||
private static readonly Sound soundRowFailed = new Sound(string.Empty, "HACKING_CLICK_BAD");
|
||||
private static readonly Sound soundSuccess = new Sound(string.Empty, "HACKING_SUCCESS");
|
||||
|
||||
private int hideTime = -1;
|
||||
private int output = 0;
|
||||
private bool firstRun = true;
|
||||
private bool inProgress = false;
|
||||
|
||||
private BruteForceBackground background = BruteForceBackground.Black;
|
||||
private string word = "LEMONADE";
|
||||
private int livesTotal = 5;
|
||||
private int livesCurrent = 5;
|
||||
private int closeAfter = -1;
|
||||
private TimeSpan end = TimeSpan.Zero;
|
||||
private TimeSpan countdown = TimeSpan.Zero;
|
||||
private bool showLives = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The Word shown to select in the menu.
|
||||
/// </summary>
|
||||
public string Word
|
||||
{
|
||||
get => word;
|
||||
set
|
||||
{
|
||||
if (value.Length != 8)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("The word needs to be exactly 8 characters long.", nameof(value));
|
||||
}
|
||||
word = value;
|
||||
CallFunction("SET_ROULETTE_WORD", value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The background of the Hacking minigame.
|
||||
/// </summary>
|
||||
public BruteForceBackground Background
|
||||
{
|
||||
get => background;
|
||||
set
|
||||
{
|
||||
background = value;
|
||||
CallFunction("SET_BACKGROUND", (int)value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The number of Lives of the minigame.
|
||||
/// </summary>
|
||||
public int TotalLives
|
||||
{
|
||||
get => livesTotal;
|
||||
set
|
||||
{
|
||||
livesTotal = value;
|
||||
if (livesCurrent > value)
|
||||
{
|
||||
livesCurrent = value;
|
||||
}
|
||||
CallFunction("SET_LIVES", livesCurrent, value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The current number of lives that the player has.
|
||||
/// </summary>
|
||||
public int CurrentLives => livesCurrent;
|
||||
/// <summary>
|
||||
/// The messages that might appear on success.
|
||||
/// </summary>
|
||||
public List<string> SuccessMessages { get; } = new List<string>();
|
||||
/// <summary>
|
||||
/// The messages that will appear when the player fails.
|
||||
/// </summary>
|
||||
public List<string> FailMessages { get; } = new List<string>();
|
||||
/// <summary>
|
||||
/// The time in milliseconds to wait before closing the Hack window automatically.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can be set to -1 to keep the Hack window open.
|
||||
/// </remarks>
|
||||
public int CloseAfter
|
||||
{
|
||||
get => closeAfter;
|
||||
set
|
||||
{
|
||||
if (value < -1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("The Closure time can't be under -1.", nameof(value));
|
||||
}
|
||||
closeAfter = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// If the player can retry the hack after failing.
|
||||
/// </summary>
|
||||
public bool CanRetry { get; set; } = false;
|
||||
/// <summary>
|
||||
/// The countdown of the Hack minigame.
|
||||
/// </summary>
|
||||
public TimeSpan Countdown
|
||||
{
|
||||
get => countdown;
|
||||
set => countdown = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// If the lives of the player should be shown on the top right.
|
||||
/// </summary>
|
||||
public bool ShowLives
|
||||
{
|
||||
get => showLives;
|
||||
set
|
||||
{
|
||||
showLives = value;
|
||||
CallFunction("SHOW_LIVES", value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// If all of the rows should be restarted after the player fails one.
|
||||
/// </summary>
|
||||
public bool ResetOnRowFail { get; set; } = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when the player finishes a hack.
|
||||
/// </summary>
|
||||
public event BruteForceFinishedEventHandler HackFinished;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Hacking Scaleform.
|
||||
/// </summary>
|
||||
public BruteForce() : base("HACKING_PC")
|
||||
{
|
||||
Visible = false;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
SetColumnSpeed(i, 100);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Resets the entire Hacking minigame.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
inProgress = true;
|
||||
|
||||
Background = background;
|
||||
RunProgram(4);
|
||||
RunProgram(83);
|
||||
TotalLives = livesTotal;
|
||||
Word = word;
|
||||
ShowLives = showLives;
|
||||
|
||||
#if RAGEMP
|
||||
int time = Misc.GetGameTimer();
|
||||
#elif RPH
|
||||
uint time = Game.GameTime;
|
||||
#else
|
||||
int time = Game.GameTime;
|
||||
#endif
|
||||
|
||||
end = TimeSpan.FromMilliseconds(time) + countdown;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the speed of one of the 8 columns.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the column.</param>
|
||||
/// <param name="speed">The speed of the column.</param>
|
||||
public void SetColumnSpeed(int index, float speed)
|
||||
{
|
||||
if (index >= 8 || index < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("The index needs to be between 0 and 7.", nameof(index));
|
||||
}
|
||||
CallFunction("SET_COLUMN_SPEED", index, speed);
|
||||
}
|
||||
/// <summary>
|
||||
/// Runs the specified Hacking program.
|
||||
/// </summary>
|
||||
/// <param name="program">The program to open.</param>
|
||||
public void RunProgram(int program)
|
||||
{
|
||||
CallFunction("RUN_PROGRAM", program);
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the information of the Hacking window.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
#if RAGEMP
|
||||
int time = Misc.GetGameTimer();
|
||||
#elif RPH
|
||||
uint time = Game.GameTime;
|
||||
#else
|
||||
int time = Game.GameTime;
|
||||
#endif
|
||||
|
||||
// If there is a time set to hide the Hack window
|
||||
if (hideTime != -1)
|
||||
{
|
||||
// If that time has already passed, go ahead and hide the window
|
||||
if (hideTime <= time)
|
||||
{
|
||||
Visible = false;
|
||||
hideTime = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the first run and is not in progress, reset it
|
||||
if (firstRun && !inProgress)
|
||||
{
|
||||
firstRun = false;
|
||||
Reset();
|
||||
}
|
||||
|
||||
// If the hack minigame is not in progress but the player can retry and he pressed enter, reset it
|
||||
if (!inProgress && CanRetry && Controls.IsJustPressed(Control.FrontendAccept))
|
||||
{
|
||||
Reset();
|
||||
hideTime = -1;
|
||||
}
|
||||
|
||||
// If the Hack minigame is in progress
|
||||
if (inProgress)
|
||||
{
|
||||
// If there is a countdown set
|
||||
if (countdown > TimeSpan.Zero)
|
||||
{
|
||||
// Calculate the time left
|
||||
TimeSpan span = countdown - (TimeSpan.FromMilliseconds(time) - end);
|
||||
|
||||
// If is lower or equal than zero, the player failed
|
||||
if (span <= TimeSpan.Zero)
|
||||
{
|
||||
CallFunction("SET_COUNTDOWN", 0, 0, 0);
|
||||
string err = FailMessages.Count == 0 ? string.Empty : FailMessages[random.Next(FailMessages.Count)];
|
||||
CallFunction("SET_ROULETTE_OUTCOME", false, err);
|
||||
hideTime = closeAfter == -1 ? -1 : (int)time + CloseAfter;
|
||||
inProgress = false;
|
||||
HackFinished?.Invoke(this, new BruteForceFinishedEventArgs(BruteForceStatus.OutOfTime));
|
||||
return;
|
||||
}
|
||||
// Otherwise, update the visible time
|
||||
else
|
||||
{
|
||||
CallFunction("SET_COUNTDOWN", span.Minutes, span.Seconds, span.Milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
// If the user pressed left, go to the left
|
||||
if (Controls.IsJustPressed(Control.MoveLeftOnly) || Controls.IsJustPressed(Control.FrontendLeft))
|
||||
{
|
||||
soundRowSwitch.PlayFrontend();
|
||||
CallFunction("SET_INPUT_EVENT", 10);
|
||||
}
|
||||
// If the user pressed right, go to the right
|
||||
else if (Controls.IsJustPressed(Control.MoveRightOnly) || Controls.IsJustPressed(Control.FrontendRight))
|
||||
{
|
||||
soundRowSwitch.PlayFrontend();
|
||||
CallFunction("SET_INPUT_EVENT", 11);
|
||||
}
|
||||
// If the user pressed accept, send the selection event
|
||||
else if (Controls.IsJustPressed(Control.FrontendAccept))
|
||||
{
|
||||
output = CallFunctionReturn("SET_INPUT_EVENT_SELECT");
|
||||
}
|
||||
|
||||
// If there is some output to receive
|
||||
if (output != 0)
|
||||
{
|
||||
// If the value is ready, go ahead and check it
|
||||
if (IsValueReady(output))
|
||||
{
|
||||
switch (GetValue<int>(output))
|
||||
{
|
||||
case 86: // Hack Completed
|
||||
string ok = SuccessMessages.Count == 0 ? string.Empty : SuccessMessages[random.Next(SuccessMessages.Count)];
|
||||
CallFunction("SET_ROULETTE_OUTCOME", true, ok);
|
||||
soundSuccess.PlayFrontend();
|
||||
HackFinished?.Invoke(this, new BruteForceFinishedEventArgs(BruteForceStatus.Completed));
|
||||
hideTime = closeAfter == -1 ? -1 : (int)time + CloseAfter;
|
||||
inProgress = false;
|
||||
break;
|
||||
case 87: // Row Failed (or lives failed)
|
||||
livesCurrent--;
|
||||
CallFunction("SET_LIVES", livesCurrent, livesTotal);
|
||||
soundRowFailed.PlayFrontend();
|
||||
if (livesCurrent <= 0)
|
||||
{
|
||||
string err = FailMessages.Count == 0 ? string.Empty : FailMessages[random.Next(FailMessages.Count)];
|
||||
CallFunction("SET_ROULETTE_OUTCOME", false, err);
|
||||
hideTime = closeAfter == -1 ? -1 : (int)time + CloseAfter;
|
||||
inProgress = false;
|
||||
HackFinished?.Invoke(this, new BruteForceFinishedEventArgs(BruteForceStatus.OutOfLives));
|
||||
}
|
||||
break;
|
||||
case 92: // Row Completed
|
||||
soundRowCompleted.PlayFrontend();
|
||||
break;
|
||||
}
|
||||
output = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
15
RageCoop.Client/LemonUI/Scaleform/IScaleform.cs
Normal file
15
RageCoop.Client/LemonUI/Scaleform/IScaleform.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace LemonUI.Scaleform
|
||||
{
|
||||
/// <summary>
|
||||
/// Scaleforms are 2D Adobe Flash-like objects.
|
||||
/// </summary>
|
||||
public interface IScaleform : IDrawable, IProcessable, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws the Scaleform in full screen.
|
||||
/// </summary>
|
||||
void DrawFullScreen();
|
||||
}
|
||||
}
|
196
RageCoop.Client/LemonUI/Scaleform/InstructionalButtons.cs
Normal file
196
RageCoop.Client/LemonUI/Scaleform/InstructionalButtons.cs
Normal file
@ -0,0 +1,196 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage.Native;
|
||||
using Control = Rage.GameControl;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LemonUI.Scaleform
|
||||
{
|
||||
/// <summary>
|
||||
/// An individual instructional button.
|
||||
/// </summary>
|
||||
public struct InstructionalButton
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private Control control;
|
||||
private string raw;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The description of this button.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// The Control used by this button.
|
||||
/// </summary>
|
||||
public Control Control
|
||||
{
|
||||
get => control;
|
||||
set
|
||||
{
|
||||
control = value;
|
||||
#if FIVEM
|
||||
raw = API.GetControlInstructionalButton(2, (int)value, 1);
|
||||
#elif RAGEMP
|
||||
raw = Invoker.Invoke<string>(Natives.GetControlInstructionalButton, 2, (int)value, 1);
|
||||
#elif RPH
|
||||
raw = (string)NativeFunction.CallByHash(0x0499D7B09FC9B407, typeof(string), 2, (int)control, 1);
|
||||
#elif SHVDN3
|
||||
raw = Function.Call<string>(Hash.GET_CONTROL_INSTRUCTIONAL_BUTTON, 2, (int)value, 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Raw Control sent to the Scaleform.
|
||||
/// </summary>
|
||||
public string Raw
|
||||
{
|
||||
get => raw;
|
||||
set
|
||||
{
|
||||
raw = value;
|
||||
control = (Control)(-1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instructional button for a Control.
|
||||
/// </summary>
|
||||
/// <param name="description">The text for the description.</param>
|
||||
/// <param name="control">The control to use.</param>
|
||||
public InstructionalButton(string description, Control control)
|
||||
{
|
||||
Description = description;
|
||||
this.control = control;
|
||||
#if FIVEM
|
||||
raw = API.GetControlInstructionalButton(2, (int)control, 1);
|
||||
#elif RAGEMP
|
||||
raw = Invoker.Invoke<string>(Natives.GetControlInstructionalButton, 2, (int)control, 1);
|
||||
#elif RPH
|
||||
raw = (string)NativeFunction.CallByHash(0x0499D7B09FC9B407, typeof(string), 2, (int)control, 1);
|
||||
#elif SHVDN3
|
||||
raw = Function.Call<string>(Hash.GET_CONTROL_INSTRUCTIONAL_BUTTON, 2, (int)control, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instructional button for a raw control.
|
||||
/// </summary>
|
||||
/// <param name="description">The text for the description.</param>
|
||||
/// <param name="raw">The raw value of the control.</param>
|
||||
public InstructionalButton(string description, string raw)
|
||||
{
|
||||
Description = description;
|
||||
control = (Control)(-1);
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Buttons shown on the bottom right of the screen.
|
||||
/// </summary>
|
||||
public class InstructionalButtons : BaseScaleform
|
||||
{
|
||||
#region Public Fields
|
||||
|
||||
/// <summary>
|
||||
/// The buttons used in this Scaleform.
|
||||
/// </summary>
|
||||
private readonly List<InstructionalButton> buttons = new List<InstructionalButton>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new set of Instructional Buttons.
|
||||
/// </summary>
|
||||
/// <param name="buttons">The buttons to add into this menu.</param>
|
||||
public InstructionalButtons(params InstructionalButton[] buttons) : base("INSTRUCTIONAL_BUTTONS")
|
||||
{
|
||||
this.buttons.AddRange(buttons);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Adds an Instructional Button.
|
||||
/// </summary>
|
||||
/// <param name="button">The button to add.</param>
|
||||
public void Add(InstructionalButton button)
|
||||
{
|
||||
// If the item is already in the list, raise an exception
|
||||
if (buttons.Contains(button))
|
||||
{
|
||||
throw new InvalidOperationException("The button is already in the Scaleform.");
|
||||
}
|
||||
|
||||
// Otherwise, add it to the list of items
|
||||
buttons.Add(button);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes an Instructional Button.
|
||||
/// </summary>
|
||||
/// <param name="button">The button to remove.</param>
|
||||
public void Remove(InstructionalButton button)
|
||||
{
|
||||
// If the button is not in the list, return
|
||||
if (!buttons.Contains(button))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, remove it
|
||||
buttons.Remove(button);
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the instructional buttons.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
buttons.Clear();
|
||||
}
|
||||
/// <summary>
|
||||
/// Refreshes the items shown in the Instructional buttons.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
// Clear all of the existing items
|
||||
CallFunction("CLEAR_ALL");
|
||||
CallFunction("TOGGLE_MOUSE_BUTTONS", 0);
|
||||
CallFunction("CREATE_CONTAINER");
|
||||
|
||||
// And add them again
|
||||
for (int i = 0; i < buttons.Count; i++)
|
||||
{
|
||||
InstructionalButton button = buttons[i];
|
||||
CallFunction("SET_DATA_SLOT", i, button.Raw, button.Description);
|
||||
}
|
||||
|
||||
CallFunction("DRAW_INSTRUCTIONAL_BUTTONS", -1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
92
RageCoop.Client/LemonUI/Scaleform/LoadingScreen.cs
Normal file
92
RageCoop.Client/LemonUI/Scaleform/LoadingScreen.cs
Normal file
@ -0,0 +1,92 @@
|
||||
namespace LemonUI.Scaleform
|
||||
{
|
||||
/// <summary>
|
||||
/// Loading screen like the transition between story mode and online.
|
||||
/// </summary>
|
||||
public class LoadingScreen : BaseScaleform
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The title of the loading screen.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
/// <summary>
|
||||
/// The subtitle of the loading screen.
|
||||
/// </summary>
|
||||
public string Subtitle { get; set; }
|
||||
/// <summary>
|
||||
/// The description of the loading screen.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
/// <summary>
|
||||
/// The Texture Dictionary (TXD) where the texture is loaded.
|
||||
/// </summary>
|
||||
public string Dictionary { get; private set; }
|
||||
/// <summary>
|
||||
/// The texture in the dictionary.
|
||||
/// </summary>
|
||||
public string Texture { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GTA Online like loading screen with no image.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the screen.</param>
|
||||
/// <param name="subtitle">The subtitle of the screen.</param>
|
||||
/// <param name="description">The description of the screen.</param>
|
||||
public LoadingScreen(string title, string subtitle, string description) : this(title, subtitle, description, string.Empty, string.Empty)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new GTA Online like loading screen with a custom texture.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the screen.</param>
|
||||
/// <param name="subtitle">The subtitle of the screen.</param>
|
||||
/// <param name="description">The description of the screen.</param>
|
||||
/// <param name="dictionary">The dictionary where the texture is located.</param>
|
||||
/// <param name="texture">The texture to use on the right.</param>
|
||||
public LoadingScreen(string title, string subtitle, string description, string dictionary, string texture) : base("GTAV_ONLINE")
|
||||
{
|
||||
// Tell the Scaleform to use the online loading screen
|
||||
CallFunction("HIDE_ONLINE_LOGO");
|
||||
CallFunction("SETUP_BIGFEED", false);
|
||||
// Save the values
|
||||
Title = title;
|
||||
Subtitle = subtitle;
|
||||
Description = description;
|
||||
Dictionary = dictionary;
|
||||
Texture = texture;
|
||||
// And send them back to the scaleform
|
||||
Update();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Changes the texture shown on the loading screen.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">The Texture Dictionary or TXD.</param>
|
||||
/// <param name="texture">The Texture name.</param>
|
||||
public void ChangeTexture(string dictionary, string texture)
|
||||
{
|
||||
Dictionary = dictionary;
|
||||
Texture = texture;
|
||||
Update();
|
||||
}
|
||||
/// <summary>
|
||||
/// Updates the Title, Description and Image of the loading screen.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
CallFunction("SET_BIGFEED_INFO", "footerStr", Description, 0, Dictionary, Texture, Subtitle, "urlDeprecated", Title);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
57
RageCoop.Client/LemonUI/Scaleform/PopUp.cs
Normal file
57
RageCoop.Client/LemonUI/Scaleform/PopUp.cs
Normal file
@ -0,0 +1,57 @@
|
||||
namespace LemonUI.Scaleform
|
||||
{
|
||||
/// <summary>
|
||||
/// A warning pop-up.
|
||||
/// </summary>
|
||||
public class PopUp : BaseScaleform
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The title of the Pop-up.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
/// <summary>
|
||||
/// The subtitle of the Pop-up.
|
||||
/// </summary>
|
||||
public string Subtitle { get; set; }
|
||||
/// <summary>
|
||||
/// The prompt of the Pop-up.
|
||||
/// </summary>
|
||||
public string Prompt { get; set; }
|
||||
/// <summary>
|
||||
/// If the black background should be shown.
|
||||
/// </summary>
|
||||
public bool ShowBackground { get; set; } = true;
|
||||
/// <summary>
|
||||
/// The error message to show.
|
||||
/// </summary>
|
||||
public string Error { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Pop-up instance.
|
||||
/// </summary>
|
||||
public PopUp() : base("POPUP_WARNING")
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Updates the texts of the Pop-up.
|
||||
/// </summary>
|
||||
public override void Update()
|
||||
{
|
||||
// first parameter "msecs" is unused
|
||||
CallFunction("SHOW_POPUP_WARNING", 0, Title, Subtitle, Prompt, ShowBackground, 0, Error);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
284
RageCoop.Client/LemonUI/Screen.cs
Normal file
284
RageCoop.Client/LemonUI/Screen.cs
Normal file
@ -0,0 +1,284 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.Native;
|
||||
using CitizenFX.Core.UI;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage;
|
||||
using Rage.Native;
|
||||
using Control = Rage.GameControl;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
using GTA.UI;
|
||||
#endif
|
||||
using LemonUI.Extensions;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the internal alignment of screen elements.
|
||||
/// </summary>
|
||||
public enum GFXAlignment
|
||||
{
|
||||
/// <summary>
|
||||
/// Vertical Alignment to the Bottom.
|
||||
/// </summary>
|
||||
Bottom = 66,
|
||||
/// <summary>
|
||||
/// Vertical Alignment to the Top.
|
||||
/// </summary>
|
||||
Top = 84,
|
||||
/// <summary>
|
||||
/// Centered Vertically or Horizontally.
|
||||
/// </summary>
|
||||
Center = 67,
|
||||
/// <summary>
|
||||
/// Horizontal Alignment to the Left.
|
||||
/// </summary>
|
||||
Left = 76,
|
||||
/// <summary>
|
||||
/// Horizontal Alignment to the Right.
|
||||
/// </summary>
|
||||
Right = 82,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains a set of tools to work with the screen information.
|
||||
/// </summary>
|
||||
public static class Screen
|
||||
{
|
||||
/// <summary>
|
||||
/// The Aspect Ratio of the screen resolution.
|
||||
/// </summary>
|
||||
public static float AspectRatio
|
||||
{
|
||||
get
|
||||
{
|
||||
#if FIVEM
|
||||
return API.GetAspectRatio(false);
|
||||
#elif RAGEMP
|
||||
return Invoker.Invoke<float>(Natives.GetAspectRatio);
|
||||
#elif RPH
|
||||
return NativeFunction.CallByHash<float>(0xF1307EF624A80D87, false);
|
||||
#elif SHVDN3
|
||||
return Function.Call<float>(Hash._GET_ASPECT_RATIO, false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The location of the cursor on screen between 0 and 1.
|
||||
/// </summary>
|
||||
public static PointF CursorPositionRelative
|
||||
{
|
||||
get
|
||||
{
|
||||
#if FIVEM
|
||||
float cursorX = API.GetControlNormal(0, (int)Control.CursorX);
|
||||
float cursorY = API.GetControlNormal(0, (int)Control.CursorY);
|
||||
#elif RAGEMP
|
||||
float cursorX = Invoker.Invoke<float>(Natives.GetControlNormal, 0, (int)Control.CursorX);
|
||||
float cursorY = Invoker.Invoke<float>(Natives.GetControlNormal, 0, (int)Control.CursorY);
|
||||
#elif RPH
|
||||
float cursorX = NativeFunction.CallByHash<float>(0xEC3C9B8D5327B563, 0, (int)Control.CursorX);
|
||||
float cursorY = NativeFunction.CallByHash<float>(0xEC3C9B8D5327B563, 0, (int)Control.CursorY);
|
||||
#elif SHVDN3
|
||||
float cursorX = Function.Call<float>(Hash.GET_CONTROL_NORMAL, 0, (int)Control.CursorX);
|
||||
float cursorY = Function.Call<float>(Hash.GET_CONTROL_NORMAL, 0, (int)Control.CursorY);
|
||||
#endif
|
||||
return new PointF(cursorX, cursorY);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a relative resolution into one scaled to 1080p.
|
||||
/// </summary>
|
||||
/// <param name="relativeX">The relative value of X.</param>
|
||||
/// <param name="relativeY">The relative value of Y.</param>
|
||||
/// <param name="absoluteX">The value of X scaled to 1080p.</param>
|
||||
/// <param name="absoluteY">The value of Y scaled to 1080p.</param>
|
||||
public static void ToAbsolute(float relativeX, float relativeY, out float absoluteX, out float absoluteY)
|
||||
{
|
||||
// Get the real width based on the aspect ratio
|
||||
float width = 1080f * AspectRatio;
|
||||
// And save the correct values
|
||||
absoluteX = width * relativeX;
|
||||
absoluteY = 1080f * relativeY;
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts a 1080p-based resolution into relative values.
|
||||
/// </summary>
|
||||
/// <param name="absoluteX">The 1080p based X coord.</param>
|
||||
/// <param name="absoluteY">The 1080p based Y coord.</param>
|
||||
/// <param name="relativeX">The value of X converted to relative.</param>
|
||||
/// <param name="relativeY">The value of Y converted to relative.</param>
|
||||
public static void ToRelative(float absoluteX, float absoluteY, out float relativeX, out float relativeY)
|
||||
{
|
||||
// Get the real width based on the aspect ratio
|
||||
float width = 1080f * AspectRatio;
|
||||
// And save the correct values
|
||||
relativeX = absoluteX / width;
|
||||
relativeY = absoluteY / 1080f;
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks if the cursor is inside of the specified area.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function takes values scaled to 1080p and is aware of the alignment set via SET_SCRIPT_GFX_ALIGN.
|
||||
/// </remarks>
|
||||
/// <param name="pos">The start of the area.</param>
|
||||
/// <param name="size">The size of the area to check.</param>
|
||||
/// <returns><see langword="true"/> if the cursor is in the specified bounds, <see langword="false"/> otherwise.</returns>
|
||||
public static bool IsCursorInArea(PointF pos, SizeF size) => IsCursorInArea(pos.X, pos.Y, size.Width, size.Height);
|
||||
/// <summary>
|
||||
/// Checks if the cursor is inside of the specified area.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function takes values scaled to 1080p and is aware of the alignment set via SET_SCRIPT_GFX_ALIGN.
|
||||
/// </remarks>
|
||||
/// <param name="x">The start X position.</param>
|
||||
/// <param name="y">The start Y position.</param>
|
||||
/// <param name="width">The height of the search area from X.</param>
|
||||
/// <param name="height">The height of the search area from Y.</param>
|
||||
/// <returns><see langword="true"/> if the cursor is in the specified bounds, <see langword="false"/> otherwise.</returns>
|
||||
public static bool IsCursorInArea(float x, float y, float width, float height)
|
||||
{
|
||||
PointF cursor = CursorPositionRelative;
|
||||
|
||||
ToRelative(width, height, out float realWidth, out float realHeight);
|
||||
|
||||
PointF realPos = GetRealPosition(x, y).ToRelative();
|
||||
|
||||
bool isX = cursor.X >= realPos.X && cursor.X <= realPos.X + realWidth;
|
||||
bool isY = cursor.Y > realPos.Y && cursor.Y < realPos.Y + realHeight;
|
||||
|
||||
return isX && isY;
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts the specified position into one that is aware of <see cref="SetElementAlignment(GFXAlignment, GFXAlignment)"/>.
|
||||
/// </summary>
|
||||
/// <param name="og">The original 1080p based position.</param>
|
||||
/// <returns>A new 1080p based position that is aware of the the Alignment.</returns>
|
||||
public static PointF GetRealPosition(PointF og) => GetRealPosition(og.X, og.Y);
|
||||
/// <summary>
|
||||
/// Converts the specified position into one that is aware of <see cref="SetElementAlignment(GFXAlignment, GFXAlignment)"/>.
|
||||
/// </summary>
|
||||
/// <param name="x">The 1080p based X position.</param>
|
||||
/// <param name="y">The 1080p based Y position.</param>
|
||||
/// <returns>A new 1080p based position that is aware of the the Alignment.</returns>
|
||||
public static PointF GetRealPosition(float x, float y)
|
||||
{
|
||||
// Convert the resolution to relative
|
||||
ToRelative(x, y, out float relativeX, out float relativeY);
|
||||
// Request the real location of the position
|
||||
float realX = 0, realY = 0;
|
||||
#if FIVEM
|
||||
API.GetScriptGfxPosition(relativeX, relativeY, ref realX, ref realY);
|
||||
#elif RAGEMP
|
||||
FloatReference argX = new FloatReference();
|
||||
FloatReference argY = new FloatReference();
|
||||
Invoker.Invoke<int>(0x6DD8F5AA635EB4B2, relativeX, relativeY, argX, argY);
|
||||
realX = argX.Value;
|
||||
realY = argY.Value;
|
||||
#elif RPH
|
||||
using (NativePointer argX = new NativePointer())
|
||||
using (NativePointer argY = new NativePointer())
|
||||
{
|
||||
NativeFunction.CallByHash<int>(0x6DD8F5AA635EB4B2, relativeX, relativeY, argX, argY);
|
||||
realX = argX.GetValue<float>();
|
||||
realY = argY.GetValue<float>();
|
||||
}
|
||||
#elif SHVDN3
|
||||
OutputArgument argX = new OutputArgument();
|
||||
OutputArgument argY = new OutputArgument();
|
||||
Function.Call((Hash)0x6DD8F5AA635EB4B2, relativeX, relativeY, argX, argY); // _GET_SCRIPT_GFX_POSITION
|
||||
realX = argX.GetResult<float>();
|
||||
realY = argY.GetResult<float>();
|
||||
#endif
|
||||
// And return it converted to absolute
|
||||
ToAbsolute(realX, realY, out float absoluteX, out float absoluteY);
|
||||
return new PointF(absoluteX, absoluteY);
|
||||
}
|
||||
/// <summary>
|
||||
/// Shows the cursor during the current game frame.
|
||||
/// </summary>
|
||||
public static void ShowCursorThisFrame()
|
||||
{
|
||||
#if FIVEM
|
||||
API.SetMouseCursorActiveThisFrame();
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xAAE7CE1D63167423);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xAAE7CE1D63167423);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash._SET_MOUSE_CURSOR_ACTIVE_THIS_FRAME);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the alignment of game elements like <see cref="Elements.ScaledRectangle"/>, <see cref="Elements.ScaledText"/> and <see cref="Elements.ScaledTexture"/>.
|
||||
/// </summary>
|
||||
/// <param name="horizontal">The Horizontal alignment of the items.</param>
|
||||
/// <param name="vertical">The vertical alignment of the items.</param>
|
||||
public static void SetElementAlignment(Alignment horizontal, GFXAlignment vertical)
|
||||
{
|
||||
// If the enum value is not correct, raise an exception
|
||||
if (!Enum.IsDefined(typeof(Alignment), horizontal))
|
||||
{
|
||||
throw new ArgumentException("Alignment is not one of the allowed values (Left, Right, Center).", nameof(horizontal));
|
||||
}
|
||||
|
||||
// Otherwise, just call the correct function
|
||||
switch (horizontal)
|
||||
{
|
||||
case Alignment.Left:
|
||||
SetElementAlignment(GFXAlignment.Left, vertical);
|
||||
break;
|
||||
case Alignment.Right:
|
||||
SetElementAlignment(GFXAlignment.Right, vertical);
|
||||
break;
|
||||
case Alignment.Center:
|
||||
SetElementAlignment(GFXAlignment.Right, vertical);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the alignment of game elements like <see cref="Elements.ScaledRectangle"/>, <see cref="Elements.ScaledText"/> and <see cref="Elements.ScaledTexture"/>.
|
||||
/// </summary>
|
||||
/// <param name="horizontal">The Horizontal alignment of the items.</param>
|
||||
/// <param name="vertical">The vertical alignment of the items.</param>
|
||||
public static void SetElementAlignment(GFXAlignment horizontal, GFXAlignment vertical)
|
||||
{
|
||||
#if FIVEM
|
||||
API.SetScriptGfxAlign((int)horizontal, (int)vertical);
|
||||
API.SetScriptGfxAlignParams(0, 0, 0, 0);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xB8A850F20A067EB6, (int)horizontal, (int)vertical);
|
||||
Invoker.Invoke(0xF5A2C681787E579D, 0, 0, 0, 0);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xB8A850F20A067EB6, (int)horizontal, (int)vertical);
|
||||
NativeFunction.CallByHash<int>(0xF5A2C681787E579D, 0, 0, 0, 0);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.SET_SCRIPT_GFX_ALIGN, (int)horizontal, (int)vertical);
|
||||
Function.Call(Hash.SET_SCRIPT_GFX_ALIGN_PARAMS, 0, 0, 0, 0);
|
||||
#endif
|
||||
}
|
||||
/// <summary>
|
||||
/// Resets the alignment of the game elements.
|
||||
/// </summary>
|
||||
public static void ResetElementAlignment()
|
||||
{
|
||||
#if FIVEM
|
||||
API.ResetScriptGfxAlign();
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(0xE3A3DB414A373DAB);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0xE3A3DB414A373DAB);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.RESET_SCRIPT_GFX_ALIGN);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
65
RageCoop.Client/LemonUI/Sound.cs
Normal file
65
RageCoop.Client/LemonUI/Sound.cs
Normal file
@ -0,0 +1,65 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core;
|
||||
using CitizenFX.Core.Native;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage;
|
||||
using Rage.Native;
|
||||
#elif SHVDN3
|
||||
using GTA;
|
||||
using GTA.Native;
|
||||
#endif
|
||||
|
||||
namespace LemonUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information for a Game Sound that is played at specific times.
|
||||
/// </summary>
|
||||
public class Sound
|
||||
{
|
||||
/// <summary>
|
||||
/// The Set where the sound is located.
|
||||
/// </summary>
|
||||
public string Set { get; set; }
|
||||
/// <summary>
|
||||
/// The name of the sound file.
|
||||
/// </summary>
|
||||
public string File { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Sound"/> class with the specified Sound Set and File.
|
||||
/// </summary>
|
||||
/// <param name="set">The Set where the sound is located.</param>
|
||||
/// <param name="file">The name of the sound file.</param>
|
||||
public Sound(string set, string file)
|
||||
{
|
||||
Set = set;
|
||||
File = file;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the sound for the local <see cref="Player"/>.
|
||||
/// </summary>
|
||||
public void PlayFrontend()
|
||||
{
|
||||
#if FIVEM
|
||||
API.PlaySoundFrontend(-1, File, Set, false);
|
||||
int id = API.GetSoundId();
|
||||
API.ReleaseSoundId(id);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.PlaySoundFrontend, -1, File, Set, false);
|
||||
int id = Invoker.Invoke<int>(Natives.GetSoundId);
|
||||
Invoker.Invoke(Natives.ReleaseSoundId, id);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x67C540AA08E4A6F5, -1, File, Set, false);
|
||||
int id = NativeFunction.CallByHash<int>(0x430386FE9BF80B45);
|
||||
NativeFunction.CallByHash<int>(0x353FC880830B88FA, id);
|
||||
#elif SHVDN3
|
||||
Function.Call(Hash.PLAY_SOUND_FRONTEND, -1, File, Set, false);
|
||||
int id = Function.Call<int>(Hash.GET_SOUND_ID);
|
||||
Function.Call(Hash.RELEASE_SOUND_ID, id);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
23
RageCoop.Client/LemonUI/TimerBars/ObjectiveSpacing.cs
Normal file
23
RageCoop.Client/LemonUI/TimerBars/ObjectiveSpacing.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace LemonUI.TimerBars
|
||||
{
|
||||
/// <summary>
|
||||
/// The spacing of the objectives in the timer bar.
|
||||
/// </summary>
|
||||
public enum ObjectiveSpacing
|
||||
{
|
||||
/// <summary>
|
||||
/// The objectives will be equally spaced.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you have way too many objectives and not enough width, you might end up with objectives overlapping each other.
|
||||
/// </remarks>
|
||||
Equal = 0,
|
||||
/// <summary>
|
||||
/// The items will all have the same spacing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you have way too many objectives and not enough width, the objectives might end up outside of the timer bar.
|
||||
/// </remarks>
|
||||
Fixed = 1
|
||||
}
|
||||
}
|
151
RageCoop.Client/LemonUI/TimerBars/TimerBar.cs
Normal file
151
RageCoop.Client/LemonUI/TimerBars/TimerBar.cs
Normal file
@ -0,0 +1,151 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core.UI;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif SHVDN3
|
||||
using GTA.UI;
|
||||
#endif
|
||||
using LemonUI.Elements;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.TimerBars
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Bar with text information shown in the bottom right.
|
||||
/// </summary>
|
||||
public class TimerBar : IDrawable
|
||||
{
|
||||
#region Constant Fields
|
||||
|
||||
/// <summary>
|
||||
/// The separation between the different timer bars.
|
||||
/// </summary>
|
||||
internal const float separation = 6.25f;
|
||||
/// <summary>
|
||||
/// The width of the background.
|
||||
/// </summary>
|
||||
internal const float backgroundWidth = 220;
|
||||
/// <summary>
|
||||
/// The height of the background.
|
||||
/// </summary>
|
||||
internal const float backgroundHeight = 37;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private string rawTitle = string.Empty;
|
||||
private string rawInfo = string.Empty;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Fields
|
||||
|
||||
/// <summary>
|
||||
/// The background of the timer bar.
|
||||
/// </summary>
|
||||
internal protected readonly ScaledTexture background = new ScaledTexture("timerbars", "all_black_bg")
|
||||
{
|
||||
Color = Color.FromArgb(160, 255, 255, 255)
|
||||
};
|
||||
/// <summary>
|
||||
/// The title of the timer bar.
|
||||
/// </summary>
|
||||
internal protected readonly ScaledText title = new ScaledText(PointF.Empty, string.Empty, 0.29f)
|
||||
{
|
||||
Alignment = Alignment.Right,
|
||||
WordWrap = 1000
|
||||
};
|
||||
/// <summary>
|
||||
/// The information of the Timer Bar.
|
||||
/// </summary>
|
||||
internal protected readonly ScaledText info = new ScaledText(PointF.Empty, string.Empty, 0.5f)
|
||||
{
|
||||
Alignment = Alignment.Right,
|
||||
WordWrap = 1000
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The title of the bar, shown on the left.
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get => rawTitle;
|
||||
set
|
||||
{
|
||||
rawTitle = value;
|
||||
title.Text = value.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The information shown on the right.
|
||||
/// </summary>
|
||||
public string Info
|
||||
{
|
||||
get => rawInfo;
|
||||
set
|
||||
{
|
||||
rawInfo = value;
|
||||
info.Text = value.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Width of the information text.
|
||||
/// </summary>
|
||||
public float InfoWidth => info.Width;
|
||||
/// <summary>
|
||||
/// The color of the information text.
|
||||
/// </summary>
|
||||
public Color Color
|
||||
{
|
||||
get => info.Color;
|
||||
set => info.Color = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="TimerBar"/> with the specified Title and Value.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the bar.</param>
|
||||
/// <param name="info">The information shown on the bar.</param>
|
||||
public TimerBar(string title, string info)
|
||||
{
|
||||
Title = title;
|
||||
Info = info;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the position of the timer bar elements based on the location of it on the screen.
|
||||
/// </summary>
|
||||
/// <param name="pos">The Top Left position of the Timer Bar.</param>
|
||||
public virtual void Recalculate(PointF pos)
|
||||
{
|
||||
background.Position = pos;
|
||||
background.Size = new SizeF(backgroundWidth, backgroundHeight);
|
||||
title.Position = new PointF(pos.X + 91, pos.Y + 8);
|
||||
info.Position = new PointF(pos.X + 218, pos.Y - 3);
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the timer bar information.
|
||||
/// </summary>
|
||||
public virtual void Draw()
|
||||
{
|
||||
background.Draw();
|
||||
title.Draw();
|
||||
info.Draw();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
174
RageCoop.Client/LemonUI/TimerBars/TimerBarCollection.cs
Normal file
174
RageCoop.Client/LemonUI/TimerBars/TimerBarCollection.cs
Normal file
@ -0,0 +1,174 @@
|
||||
#if FIVEM
|
||||
using CitizenFX.Core.UI;
|
||||
#elif RAGEMP
|
||||
using RAGE.Game;
|
||||
#elif RPH
|
||||
using Rage;
|
||||
using Rage.Native;
|
||||
#elif SHVDN3
|
||||
using GTA.UI;
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.TimerBars
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection or Set of <see cref="TimerBar"/>.
|
||||
/// </summary>
|
||||
public class TimerBarCollection : IContainer<TimerBar>
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// If this collection of Timer Bars is visible to the user.
|
||||
/// </summary>
|
||||
public bool Visible { get; set; } = true;
|
||||
/// <summary>
|
||||
/// The <see cref="TimerBar"/>s that are part of this collection.
|
||||
/// </summary>
|
||||
public List<TimerBar> TimerBars { get; } = new List<TimerBar>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new collection of Timer Bars.
|
||||
/// </summary>
|
||||
/// <param name="bars"></param>
|
||||
public TimerBarCollection(params TimerBar[] bars)
|
||||
{
|
||||
TimerBars.AddRange(bars);
|
||||
Recalculate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="TimerBar"/> onto this collection.
|
||||
/// </summary>
|
||||
/// <param name="bar">The <see cref="TimerBar"/> to add.</param>
|
||||
public void Add(TimerBar bar)
|
||||
{
|
||||
// If the item is already on the list, raise an exception
|
||||
if (TimerBars.Contains(bar))
|
||||
{
|
||||
throw new InvalidOperationException("The item is already part of the menu.");
|
||||
}
|
||||
// Also raise an exception if is null
|
||||
if (bar == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bar));
|
||||
}
|
||||
// If we got here, add it
|
||||
TimerBars.Add(bar);
|
||||
// And recalculate the positions of the existing items
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes a <see cref="TimerBar"/> from the Collection.
|
||||
/// </summary>
|
||||
/// <param name="bar">The <see cref="TimerBar"/> to remove.</param>
|
||||
public void Remove(TimerBar bar)
|
||||
{
|
||||
// If the bar is not present, return
|
||||
if (!TimerBars.Contains(bar))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, remove it
|
||||
TimerBars.Remove(bar);
|
||||
// And recalculate the positions
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the <see cref="TimerBar"/> that match the function.
|
||||
/// </summary>
|
||||
/// <param name="func">The function to check the <see cref="TimerBar"/>.</param>
|
||||
public void Remove(Func<TimerBar, bool> func)
|
||||
{
|
||||
// Iterate over the timer bars
|
||||
foreach (TimerBar bar in new List<TimerBar>(TimerBars))
|
||||
{
|
||||
// If it matches the function, remove it
|
||||
if (func(bar))
|
||||
{
|
||||
TimerBars.Remove(bar);
|
||||
}
|
||||
}
|
||||
// Finally, recalculate the positions
|
||||
Recalculate();
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes all of the <see cref="TimerBar"/> in this collection.
|
||||
/// </summary>
|
||||
public void Clear() => TimerBars.Clear();
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="TimerBar"/> is part of this collection.
|
||||
/// </summary>
|
||||
/// <param name="bar">The <see cref="TimerBar"/> to check.</param>
|
||||
public bool Contains(TimerBar bar) => TimerBars.Contains(bar);
|
||||
/// <summary>
|
||||
/// Recalculates the positions and sizes of the <see cref="TimerBar"/>.
|
||||
/// </summary>
|
||||
public void Recalculate()
|
||||
{
|
||||
// Get the position of 0,0 while staying safe zone aware
|
||||
Screen.SetElementAlignment(GFXAlignment.Right, GFXAlignment.Bottom);
|
||||
PointF pos = Screen.GetRealPosition(PointF.Empty);
|
||||
Screen.ResetElementAlignment();
|
||||
|
||||
// Iterate over the existing timer bars and save the count
|
||||
int count = 0;
|
||||
foreach (TimerBar timerBar in TimerBars)
|
||||
{
|
||||
// And send them to the timer bar
|
||||
timerBar.Recalculate(new PointF(pos.X - TimerBar.backgroundWidth, pos.Y - (TimerBar.backgroundHeight * (TimerBars.Count - count)) - (TimerBar.separation * (TimerBars.Count - count - 1))));
|
||||
// Finish by increasing the total count of items
|
||||
count++;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the known timer bars.
|
||||
/// </summary>
|
||||
public void Process()
|
||||
{
|
||||
// If there are no timer bars or the collection is disabled, return
|
||||
if (TimerBars.Count == 0 || !Visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the texts in the bottom right corner of the screen
|
||||
#if FIVEM
|
||||
CitizenFX.Core.UI.Screen.Hud.HideComponentThisFrame(HudComponent.AreaName);
|
||||
CitizenFX.Core.UI.Screen.Hud.HideComponentThisFrame(HudComponent.StreetName);
|
||||
CitizenFX.Core.UI.Screen.Hud.HideComponentThisFrame(HudComponent.VehicleName);
|
||||
#elif RAGEMP
|
||||
Invoker.Invoke(Natives.HideHudComponentThisFrame, HudComponent.AreaName);
|
||||
Invoker.Invoke(Natives.HideHudComponentThisFrame, HudComponent.StreetName);
|
||||
Invoker.Invoke(Natives.HideHudComponentThisFrame, HudComponent.VehicleName);
|
||||
#elif RPH
|
||||
NativeFunction.CallByHash<int>(0x6806C51AD12B83B8, 7);
|
||||
NativeFunction.CallByHash<int>(0x6806C51AD12B83B8, 9);
|
||||
NativeFunction.CallByHash<int>(0x6806C51AD12B83B8, 6);
|
||||
#elif SHVDN3
|
||||
Hud.HideComponentThisFrame(HudComponent.AreaName);
|
||||
Hud.HideComponentThisFrame(HudComponent.StreetName);
|
||||
Hud.HideComponentThisFrame(HudComponent.VehicleName);
|
||||
#endif
|
||||
// Draw the existing timer bars
|
||||
foreach (TimerBar timerBar in TimerBars)
|
||||
{
|
||||
timerBar.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
220
RageCoop.Client/LemonUI/TimerBars/TimerBarObjective.cs
Normal file
220
RageCoop.Client/LemonUI/TimerBars/TimerBarObjective.cs
Normal file
@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using LemonUI.Elements;
|
||||
|
||||
namespace LemonUI.TimerBars
|
||||
{
|
||||
/// <summary>
|
||||
/// A timer bar for a specific amount of objectives.
|
||||
/// </summary>
|
||||
public class TimerBarObjective : TimerBar
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private const float width = 20;
|
||||
private const float height = 20;
|
||||
|
||||
private static readonly Color colorWhite = Color.FromArgb(255, 255, 255);
|
||||
private static readonly Color colorCompleted = Color.FromArgb(101, 180, 212);
|
||||
|
||||
private readonly List<ScaledTexture> objectives = new List<ScaledTexture>();
|
||||
|
||||
private PointF lastPosition = default;
|
||||
|
||||
private int count = 1;
|
||||
private int completed = 0;
|
||||
private Color colorSet = colorCompleted;
|
||||
private ObjectiveSpacing objectiveSpacing = ObjectiveSpacing.Equal;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// The number of objectives shown in the timer bar.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get => count;
|
||||
set
|
||||
{
|
||||
if (count == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "The number of objectives can't be under or equal to zero.");
|
||||
}
|
||||
|
||||
count = value;
|
||||
UpdateObjectiveCount();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The number of completed objectives.
|
||||
/// </summary>
|
||||
public int Completed
|
||||
{
|
||||
get => completed;
|
||||
set
|
||||
{
|
||||
if (completed == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "The number of completed objectives can't be under zero.");
|
||||
}
|
||||
if (value > Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), "The number of completed objectives can't be over the total number of objectives.");
|
||||
}
|
||||
|
||||
completed = value;
|
||||
|
||||
UpdateObjectiveColors();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The color used for completed objectives.
|
||||
/// </summary>
|
||||
public Color CompletedColor
|
||||
{
|
||||
get => colorSet;
|
||||
set
|
||||
{
|
||||
if (colorSet == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
colorSet = value;
|
||||
UpdateObjectiveColors();
|
||||
}
|
||||
}
|
||||
|
||||
public ObjectiveSpacing Spacing
|
||||
{
|
||||
get => objectiveSpacing;
|
||||
set
|
||||
{
|
||||
if (objectiveSpacing == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
objectiveSpacing = value;
|
||||
Recalculate(lastPosition);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new timer bar used to show objectives.
|
||||
/// </summary>
|
||||
public TimerBarObjective(string title) : base(title, string.Empty)
|
||||
{
|
||||
UpdateObjectiveCount();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
private void UpdateObjectiveCount()
|
||||
{
|
||||
// just to make sure
|
||||
if (completed > count)
|
||||
{
|
||||
completed = count;
|
||||
}
|
||||
|
||||
objectives.Clear();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
objectives.Add(new ScaledTexture("timerbars", "circle_checkpoints"));
|
||||
}
|
||||
|
||||
UpdateObjectiveColors();
|
||||
}
|
||||
private void UpdateObjectiveColors()
|
||||
{
|
||||
for (int i = 0; i < objectives.Count; i++)
|
||||
{
|
||||
ScaledTexture texture = objectives[i];
|
||||
texture.Color = i < completed ? colorSet : colorWhite;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
/// <summary>
|
||||
/// Draws the objective timer bar.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
background.Draw();
|
||||
title.Draw();
|
||||
|
||||
foreach (ScaledTexture texture in objectives)
|
||||
{
|
||||
texture.Draw();
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override void Recalculate(PointF pos)
|
||||
{
|
||||
lastPosition = pos;
|
||||
|
||||
base.Recalculate(pos);
|
||||
|
||||
const float safe = width + 5;
|
||||
float startY = pos.Y + (backgroundHeight * 0.5f) - (height * 0.5f);
|
||||
|
||||
switch (objectiveSpacing)
|
||||
{
|
||||
case ObjectiveSpacing.Equal:
|
||||
{
|
||||
const float half = backgroundWidth * 0.5f;
|
||||
float startX = pos.X + half;
|
||||
float spacingWidth = (half - safe) / (objectives.Count - 1);
|
||||
|
||||
for (int i = 0; i < objectives.Count; i++)
|
||||
{
|
||||
ScaledTexture texture = objectives[i];
|
||||
texture.Size = new SizeF(width, height);
|
||||
texture.Position = new PointF(startX + (spacingWidth * i), startY);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ObjectiveSpacing.Fixed:
|
||||
{
|
||||
float startX = pos.X + backgroundWidth - safe - (width * (count - 1));
|
||||
|
||||
for (int i = 0; i < objectives.Count; i++)
|
||||
{
|
||||
ScaledTexture texture = objectives[i];
|
||||
texture.Size = new SizeF(width, height);
|
||||
texture.Position = new PointF(startX + (i * width), startY);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
123
RageCoop.Client/LemonUI/TimerBars/TimerBarProgress.cs
Normal file
123
RageCoop.Client/LemonUI/TimerBars/TimerBarProgress.cs
Normal file
@ -0,0 +1,123 @@
|
||||
using LemonUI.Elements;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace LemonUI.TimerBars
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Timer Bar that shows the progress of something.
|
||||
/// </summary>
|
||||
public class TimerBarProgress : TimerBar
|
||||
{
|
||||
#region Constant Fields
|
||||
|
||||
private const float barWidth = 108;
|
||||
private const float barHeight = 15;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private float progress = 100;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Fields
|
||||
|
||||
/// <summary>
|
||||
/// The background of the Progress Bar.
|
||||
/// </summary>
|
||||
internal protected readonly ScaledRectangle barBackground = new ScaledRectangle(PointF.Empty, SizeF.Empty)
|
||||
{
|
||||
Color = Color.FromArgb(255, 139, 0, 0)
|
||||
};
|
||||
/// <summary>
|
||||
/// The foreground of the Progress Bar.
|
||||
/// </summary>
|
||||
internal protected readonly ScaledRectangle barForeground = new ScaledRectangle(PointF.Empty, SizeF.Empty)
|
||||
{
|
||||
Color = Color.FromArgb(255, 255, 0, 0)
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// The progress of the bar.
|
||||
/// </summary>
|
||||
public float Progress
|
||||
{
|
||||
get => progress;
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > 100)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
}
|
||||
progress = value;
|
||||
barForeground.Size = new SizeF(barWidth * (value * 0.01f), barHeight);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The Foreground color of the Progress bar.
|
||||
/// </summary>
|
||||
public Color ForegroundColor
|
||||
{
|
||||
get => barForeground.Color;
|
||||
set => barForeground.Color = value;
|
||||
}
|
||||
/// <summary>
|
||||
/// The Background color of the Progress bar.
|
||||
/// </summary>
|
||||
public Color BackgroundColor
|
||||
{
|
||||
get => barBackground.Color;
|
||||
set => barBackground.Color = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="TimerBarProgress"/> with the specified title.
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the bar.</param>
|
||||
public TimerBarProgress(string title) : base(title, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Functions
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the position of the timer bar elements based on the location of it on the screen.
|
||||
/// </summary>
|
||||
/// <param name="pos">The Top Left position of the Timer Bar.</param>
|
||||
public override void Recalculate(PointF pos)
|
||||
{
|
||||
// Recalculate the base elements
|
||||
base.Recalculate(pos);
|
||||
// And set the size and position of the progress bar
|
||||
PointF barPos = new PointF(pos.X + 103, pos.Y + 12);
|
||||
barBackground.Position = barPos;
|
||||
barBackground.Size = new SizeF(barWidth, barHeight);
|
||||
barForeground.Position = barPos;
|
||||
barForeground.Size = new SizeF(barWidth * (progress * 0.01f), barHeight);
|
||||
}
|
||||
/// <summary>
|
||||
/// Draws the TimerBar.
|
||||
/// </summary>
|
||||
public override void Draw()
|
||||
{
|
||||
background.Draw();
|
||||
title.Draw();
|
||||
barBackground.Draw();
|
||||
barForeground.Draw();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -79,7 +79,6 @@ namespace RageCoop.Client
|
||||
#if !NON_INTERACTIVE
|
||||
#endif
|
||||
MainChat = new Chat();
|
||||
|
||||
Tick += OnTick;
|
||||
Tick += (s,e) => { Scripting.API.Events.InvokeTick(); };
|
||||
KeyDown += OnKeyDown;
|
||||
|
@ -58,8 +58,6 @@ namespace RageCoop.Client
|
||||
config.EnableMessageType(NetIncomingMessageType.ConnectionLatencyUpdated);
|
||||
config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
|
||||
|
||||
Client = new NetClient(config);
|
||||
Client.Start();
|
||||
|
||||
string[] ip = new string[2];
|
||||
|
||||
@ -78,8 +76,11 @@ namespace RageCoop.Client
|
||||
EntityPool.AddPlayer();
|
||||
Task.Run(() =>
|
||||
{
|
||||
GetServerPublicKey(address);
|
||||
Client = new NetClient(config);
|
||||
Client.Start();
|
||||
|
||||
Security.Regen();
|
||||
GetServerPublicKey(address);
|
||||
|
||||
// Send HandshakePacket
|
||||
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
|
||||
@ -134,7 +135,6 @@ namespace RageCoop.Client
|
||||
{
|
||||
var msg=Client.CreateMessage();
|
||||
new Packets.PublicKeyRequest().Pack(msg);
|
||||
|
||||
var adds =address.Split(':');
|
||||
Client.SendUnconnectedMessage(msg,adds[0],int.Parse(adds[1]));
|
||||
PublicKeyReceived.WaitOne(timeout);
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Any CPU</Platform>
|
||||
<PublishDir>bin\Release\publish\win-x64\</PublishDir>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>True</PublishSingleFile>
|
||||
<PublishReadyToRun>True</PublishReadyToRun>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<History>True|2022-06-27T04:41:08.2707244Z;True|2022-06-27T12:39:50.9236964+08:00;True|2022-06-27T12:37:12.3619963+08:00;True|2022-06-27T12:36:59.5077744+08:00;True|2022-06-27T12:35:49.2538484+08:00;</History>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
@ -12,7 +13,7 @@
|
||||
<AssemblyVersion>0.5.0</AssemblyVersion>
|
||||
<FileVersion>0.5.0</FileVersion>
|
||||
<Version>0.5.0</Version>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<Authors>RAGECOOP</Authors>
|
||||
<Description>An API reference for developing client-side resource for RAGECOOP</Description>
|
||||
<PackageProjectUrl>https://ragecoop.online/</PackageProjectUrl>
|
||||
@ -20,6 +21,7 @@
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<DefineConstants>SHVDN3</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@ -38,16 +40,15 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="LemonUI.SHVDN3">
|
||||
<HintPath>..\libs\LemonUI.SHVDN3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet">
|
||||
<HintPath>..\..\RageCoop.SHVDN\bin\Release\ScriptHookVDotNet.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
6
RageCoop.Client/RageCoop.Client.csproj.user
Normal file
6
RageCoop.Client/RageCoop.Client.csproj.user
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_LastSelectedProfileId>M:\SandBox-Shared\repo\RageCoop\RageCoop-V\RageCoop.Client\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -36,5 +36,10 @@ namespace RageCoop.Client
|
||||
para.Exponent = exponent;
|
||||
ServerRSA=RSA.Create(para);
|
||||
}
|
||||
public void Regen()
|
||||
{
|
||||
ClientAes.GenerateKey();
|
||||
ClientAes.GenerateIV();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@
|
||||
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ScriptHookVDotNet3">
|
||||
<HintPath>..\libs\ScriptHookVDotNet3.dll</HintPath>
|
||||
<HintPath>..\..\RageCoop.SHVDN\bin\Release\ScriptHookVDotNet3.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -609,7 +609,7 @@ namespace RageCoop.Server
|
||||
API.Events.InvokePlayerDisconnected(localClient);
|
||||
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.ID}");
|
||||
Clients.Remove(localClient.NetID);
|
||||
|
||||
Security.RemoveConnection(localClient.Connection.RemoteEndPoint);
|
||||
}
|
||||
|
||||
#region SyncEntities
|
||||
|
Loading…
x
Reference in New Issue
Block a user