#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 { /// /// Represents the method that reports a Resolution change in the Game Settings. /// /// The source of the event. /// A containing the Previous and Current resolution. public delegate void ResolutionChangedEventHandler(object sender, ResolutionChangedEventArgs e); /// /// Represents the method that reports a Safe Zone change in the Game Settings. /// /// The source of the event event. /// A containing the Previous and Current Safe Zone. public delegate void SafeZoneChangedEventHandler(object sender, SafeZoneChangedEventArgs e); /// /// Represents the information after a Resolution Change in the game. /// public class ResolutionChangedEventArgs { /// /// The Game Resolution before it was changed. /// public SizeF Before { get; } /// /// The Game Resolution after it was changed. /// public SizeF After { get; } internal ResolutionChangedEventArgs(SizeF before, SizeF after) { Before = before; After = after; } } /// /// Represents the information after a Safe Zone Change in the game. /// public class SafeZoneChangedEventArgs { /// /// The raw Safezone size before the change. /// public float Before { get; } /// /// The Safezone size after the change. /// public float After { get; } internal SafeZoneChangedEventArgs(float before, float after) { Before = before; After = after; } } /// /// Manager for Menus and Items. /// public class ObjectPool { #region Private Fields /// /// The last known resolution by the object pool. /// #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 /// /// The last know Safezone size. /// #if FIVEM private float lastKnownSafezone = API.GetSafeZoneSize(); #elif RAGEMP private float lastKnownSafezone = Invoker.Invoke(Natives.GetSafeZoneSize); #elif RPH private float lastKnownSafezone = NativeFunction.CallByHash(0xBAF107B6BB2C97F0); #elif SHVDN3 private float lastKnownSafezone = Function.Call(Hash.GET_SAFE_ZONE_SIZE); #endif /// /// The list of processable objects. /// private readonly ConcurrentDictionary objects = new ConcurrentDictionary(); #endregion #region Public Properties /// /// Checks if there are objects visible on the screen. /// 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 /// /// Event triggered when the game resolution is changed. /// public event ResolutionChangedEventHandler ResolutionChanged; /// /// Event triggered when the Safezone size option in the Display settings is changed. /// public event SafeZoneChangedEventHandler SafezoneChanged; #endregion #region Tools /// /// Detects resolution changes by comparing the last known resolution and the current one. /// 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; } } /// /// Detects Safezone changes by comparing the last known value to the current one. /// private void DetectSafezoneChanges() { // Get the current Safezone size #if FIVEM float safezone = API.GetSafeZoneSize(); #elif RAGEMP float safezone = Invoker.Invoke(Natives.GetSafeZoneSize); #elif RPH float safezone = NativeFunction.CallByHash(0xBAF107B6BB2C97F0); #elif SHVDN3 float safezone = Function.Call(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 /// /// Adds the object into the pool. /// /// The object to add. 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); } /// /// Removes the object from the pool. /// /// The object to remove. public void Remove(IProcessable obj) { objects.TryRemove(obj.GetHashCode(), out _); } /// /// Performs the specified action on each element that matches T. /// /// The type to match. /// The action delegate to perform on each T. public void ForEach(Action action) { foreach (IProcessable obj in objects.Values) { if (obj is T conv) { action(conv); } } } /// /// Refreshes all of the items. /// public void RefreshAll() { // Iterate over the objects and recalculate those possible foreach (IProcessable obj in objects.Values) { if (obj is IRecalculable recal) { recal.Recalculate(); } } } /// /// Hides all of the objects. /// public void HideAll() { foreach (IProcessable obj in objects.Values) { obj.Visible = false; } } /// /// Processes the objects and features in this pool. /// This needs to be called every tick. /// 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 } }