Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions PathfinderAPI/Event/Options/CustomOptionsLoadEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Pathfinder.Event.Options;

public class CustomOptionsLoadEvent : PathfinderEvent
{
public CustomOptionsLoadEvent() { }
}
6 changes: 3 additions & 3 deletions PathfinderAPI/Meta/Load/HacknetPluginExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ public static class HacknetPluginExtensions
{
public static string GetOptionsTag(this HacknetPlugin plugin)
{
if(!OptionsTabAttribute.pluginToOptionsTag.TryGetValue(plugin, out var tag))
if(!OptionsTabAttribute.pluginToOptTabAttribute.TryGetValue(plugin, out var attr))
return null;
return tag;
return attr.TabName;
}

public static bool HasOptionsTag(this HacknetPlugin plugin)
{
return OptionsTabAttribute.pluginToOptionsTag.ContainsKey(plugin);
return OptionsTabAttribute.pluginToOptTabAttribute.ContainsKey(plugin);
}
}
39 changes: 23 additions & 16 deletions PathfinderAPI/Meta/Load/OptionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,48 +7,55 @@ namespace Pathfinder.Meta.Load;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class OptionAttribute : BaseAttribute
{
public string Tag { get; set; }
[Obsolete("Use TabName")]
public string Tag { get => TabName; set => TabName = value; }
public string TabName { get; set; }
public string TabId { get; set; }

public OptionAttribute(string tag = null)
public OptionAttribute(string tag = null, string tabId = null)
{
this.Tag = tag;
TabName = tag;
TabId = tabId;
}

public OptionAttribute(Type pluginType)
{
this.Tag = pluginType.GetCustomAttribute<OptionsTabAttribute>()?.Tag;
var tabAttr = pluginType.GetCustomAttribute<OptionsTabAttribute>();
TabName = tabAttr.TabName;
TabId = tabAttr.TabId;
}

protected internal override void CallOn(HacknetPlugin plugin, MemberInfo targettedInfo)
{
if(Tag == null)
if(TabName == null)
{
Tag = plugin.GetOptionsTag();
if(Tag == null)
if(!OptionsTabAttribute.pluginToOptTabAttribute.TryGetValue(plugin, out var tab))
throw new InvalidOperationException($"Could not find Pathfinder.Meta.Load.OptionsTabAttribute for {targettedInfo.DeclaringType.FullName}");
TabName = tab.TabName;
TabId = tab.TabId;
}

if(targettedInfo.DeclaringType != plugin.GetType())
throw new InvalidOperationException($"Pathfinder.Meta.Load.OptionAttribute is only valid in a class derived from BepInEx.Hacknet.HacknetPlugin");

Option option = null;
IPluginOption option = null;
switch(targettedInfo)
{
case PropertyInfo propertyInfo:
if(!propertyInfo.PropertyType.IsSubclassOf(typeof(Option)))
throw new InvalidOperationException($"Property {propertyInfo.Name}'s type does not derive from Pathfinder.Options.Option");
option = (Option)(propertyInfo.GetGetMethod()?.Invoke(plugin, null));
if(!typeof(IPluginOption).IsAssignableFrom(propertyInfo.PropertyType))
throw new InvalidOperationException($"Property {propertyInfo.Name}'s type does not derive from Pathfinder.Options.IPluginOption");
option = (IPluginOption)(propertyInfo.GetGetMethod()?.Invoke(plugin, null));
break;
case FieldInfo fieldInfo:
if(!fieldInfo.FieldType.IsSubclassOf(typeof(Option)))
throw new InvalidOperationException($"Field {fieldInfo.Name}'s type does not derive from Pathfinder.Options.Option");
option = (Option)fieldInfo.GetValue(plugin);
if(!typeof(IPluginOption).IsAssignableFrom(fieldInfo.FieldType))
throw new InvalidOperationException($"Field {fieldInfo.Name}'s type does not derive from Pathfinder.Options.IPluginOption");
option = (IPluginOption)fieldInfo.GetValue(plugin);
break;
}

if(option == null)
throw new InvalidOperationException($"Option not set to a default value, Option members should be set before HacknetPlugin.Load() is called");
throw new InvalidOperationException($"IPluginOption not set to a default value, IPluginOption members should be set before HacknetPlugin.Load() is called");

OptionsManager.AddOption(Tag, option);
OptionsManager.GetOrRegisterTab(plugin, TabName, TabId).AddOption(option);
}
}
14 changes: 9 additions & 5 deletions PathfinderAPI/Meta/Load/OptionsTabAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ namespace Pathfinder.Meta.Load;
[AttributeUsage(AttributeTargets.Class)]
public class OptionsTabAttribute : BaseAttribute
{
internal static readonly Dictionary<HacknetPlugin, string> pluginToOptionsTag = new Dictionary<HacknetPlugin, string>();
internal static readonly Dictionary<HacknetPlugin, OptionsTabAttribute> pluginToOptTabAttribute = new Dictionary<HacknetPlugin, OptionsTabAttribute>();

public string Tag { get; }
[Obsolete("Use TabName")]
public string Tag { get => TabName; set => TabName = value; }
public string TabName { get; set; }
public string TabId { get; set; }

public OptionsTabAttribute(string tag)
public OptionsTabAttribute(string tag, string tabId = null)
{
this.Tag = tag;
TabName = tag;
TabId = tabId;
}

protected internal override void CallOn(HacknetPlugin plugin, MemberInfo targettedInfo)
{
pluginToOptionsTag.Add(plugin, Tag);
pluginToOptTabAttribute.Add(plugin, this);
}
}
2 changes: 1 addition & 1 deletion PathfinderAPI/MiscPatches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private static void NoSteamErrorMessageIL(ILContext il)
c.RemoveRange(3);

c.Emit(OpCodes.Ldsfld, AccessTools.Field(typeof(PathfinderOptions), nameof(PathfinderOptions.DisableSteamCloudError)));
c.Emit(OpCodes.Ldfld, AccessTools.Field(typeof(OptionCheckbox), nameof(OptionCheckbox.Value)));
c.Emit(OpCodes.Callvirt, AccessTools.Method(typeof(PluginCheckbox), $"get_{nameof(PluginCheckbox.Value)}"));

c.GotoNext(MoveType.Before, x => x.MatchLdstr(out _));
c.Next.Operand = "Steam Cloud saving disabled by Pathfinder";
Expand Down
135 changes: 135 additions & 0 deletions PathfinderAPI/Options/BasePluginOption.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using BepInEx.Configuration;
using Pathfinder.GUI;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Hacknet;

namespace Pathfinder.Options;

public interface IPluginOption
{
PluginOptionTab Tab { get; set; }
public ConfigEntryBase ConfigEntry { get; }
string Id { get; }
Rectangle Rectangle { get; set; }
Vector2 Size { get; }
bool TrySetOffset(Vector2 offset);
void LoadContent();
void OnDraw(GameTime gameTime);
}

public abstract class BasePluginOption<ValueT> : IPluginOption
{
public PluginOptionTab Tab { get; set; }
public virtual ConfigEntryBase ConfigEntry => TypedConfigEntry;
public virtual ConfigEntry<ValueT> TypedConfigEntry { get; protected set; }

public Rectangle Rectangle { get; set; }
public int HacknetGuiId { get; private set; }

public virtual ValueT Value
{
get
{
if(TypedConfigEntry == null)
{
if(Tab.Plugin.Config.TryGetEntry<ValueT>(Tab.Id, Id, out var entry))
TypedConfigEntry = entry;
else
TypedConfigEntry = Tab.Plugin.Config.Bind<ValueT>(Tab.Id, Id, DefaultValue, ConfigDescription ?? "");
}
return TypedConfigEntry.Value;
}
set
{
if(TypedConfigEntry == null)
TypedConfigEntry = Tab.Plugin.Config.Bind<ValueT>(Tab.Id, Id, DefaultValue, ConfigDescription ?? "");
TypedConfigEntry.Value = value;
}
}
public virtual ValueT DefaultValue { get; set; } = default;
public virtual string HeaderText { get; protected set; }
public virtual string DescriptionText { get; protected set; }
public virtual string ConfigDescription { get; protected set; }
public virtual string Id { get; }

public Color HeaderColor { get; set; } = Color.White;
public Color DescriptionColor { get; set; } = Color.White;

public SpriteFont HeaderFont { get; set; }
public SpriteFont DescriptionFont { get; set; }

public Vector2 HeaderTextSize => HeaderFont.MeasureString(HeaderText);
public Vector2 DescriptionTextSize => DescriptionFont.MeasureString(DescriptionText);

public virtual Vector2 Position => new Vector2(Rectangle.X + Offset.X, Rectangle.Y + Offset.Y);
public virtual Vector2 Offset { get; set; }

public Vector2 Size
{
get
{
var minsize = MinSize;
return new Vector2(Math.Max(Rectangle.Width, minsize.X), Math.Max(Rectangle.Height, minsize.Y));
}
}
public virtual Vector2 MinSize
{
get
{
var headerSize = HeaderTextSize;
var descSize = DescriptionTextSize;
return new Vector2(Math.Max(headerSize.X, descSize.X), headerSize.Y + descSize.Y);
}
}

protected static string MakeIdFrom(string name, string id)
=> id ?? string.Concat(name.Where(c => !char.IsWhiteSpace(c) && c != '='));

protected BasePluginOption(string headerText, string descriptionText = null, ValueT defaultValue = default, string configDesc = null, string id = null)
{
HeaderText = headerText;
DescriptionText = descriptionText;
DefaultValue = defaultValue;
ConfigDescription = configDesc;
Id = MakeIdFrom(headerText, id);
}

public bool TrySetOffset(Vector2 offset)
{
Offset = offset;
return Offset == offset;
}

public bool TrySetHeaderText(string text)
{
HeaderText = text;
return HeaderText == text;
}

public bool TrySetDescriptionText(string text)
{
DescriptionText = text;
return DescriptionText == text;
}

public bool TrySetConfigDescription(string desc)
{
ConfigDescription = desc;
return ConfigDescription == desc;
}

public virtual void LoadContent()
{
HacknetGuiId = PFButton.GetNextID();
HeaderFont ??= GuiData.font;
DescriptionFont ??= GuiData.smallfont;
}

public abstract void OnDraw(GameTime gameTime);

protected void DrawString(Vector2 pos, string text, Color? color = null, SpriteFont font = null)
{
Tab.Batch.DrawString(font ?? GuiData.font, text, pos, color ?? Color.White);
}
}
4 changes: 4 additions & 0 deletions PathfinderAPI/Options/Options.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#pragma warning disable 618

using Hacknet.Gui;
using Microsoft.Xna.Framework;
using Pathfinder.GUI;

namespace Pathfinder.Options;

[Obsolete("Use BasePluginOption")]
public abstract class Option
{
public string Name;
Expand All @@ -22,6 +25,7 @@ public Option(string name, string description="")
public abstract void Draw(int x, int y);
}

[Obsolete("Use PluginCheckbox")]
public class OptionCheckbox : Option
{
public bool Value;
Expand Down
Loading