mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add better chat command handling, help, and option to set basic mod state.
This commit is contained in:
parent
1ae96c71a3
commit
6f356105cc
7 changed files with 719 additions and 227 deletions
|
|
@ -60,6 +60,15 @@ public sealed partial class ActorManager : IDisposable
|
||||||
public string ToWorldName(ushort worldId)
|
public string ToWorldName(ushort worldId)
|
||||||
=> worldId == ushort.MaxValue ? "Any World" : Worlds.TryGetValue(worldId, out var name) ? name : "Invalid";
|
=> worldId == ushort.MaxValue ? "Any World" : Worlds.TryGetValue(worldId, out var name) ? name : "Invalid";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the world id corresponding to the given name.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>ushort.MaxValue if the name is empty, 0 if it is not a valid world, or the worlds id.</returns>
|
||||||
|
public ushort ToWorldId(string worldName)
|
||||||
|
=> worldName.Length != 0
|
||||||
|
? Worlds.FirstOrDefault(kvp => string.Equals(kvp.Value, worldName, StringComparison.OrdinalIgnoreCase), default).Key
|
||||||
|
: ushort.MaxValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert a given ID for a certain ObjectKind to a name.
|
/// Convert a given ID for a certain ObjectKind to a name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
@ -106,6 +107,139 @@ public partial class ActorManager
|
||||||
return main;
|
return main;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class IdentifierParseError : Exception
|
||||||
|
{
|
||||||
|
public IdentifierParseError(string reason)
|
||||||
|
: base(reason)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActorIdentifier FromUserString(string userString)
|
||||||
|
{
|
||||||
|
if (userString.Length == 0)
|
||||||
|
throw new IdentifierParseError("The identifier string was empty.");
|
||||||
|
|
||||||
|
var split = userString.Split('|', 3, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (split.Length < 2)
|
||||||
|
throw new IdentifierParseError($"The identifier string {userString} does not contain a type and a value.");
|
||||||
|
|
||||||
|
var type = IdentifierType.Invalid;
|
||||||
|
var playerName = ByteString.Empty;
|
||||||
|
ushort worldId = 0;
|
||||||
|
var kind = ObjectKind.Player;
|
||||||
|
var objectId = 0u;
|
||||||
|
|
||||||
|
(ByteString, ushort) ParsePlayer(string player)
|
||||||
|
{
|
||||||
|
var parts = player.Split('@', 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (!VerifyPlayerName(parts[0]))
|
||||||
|
throw new IdentifierParseError($"{parts[0]} is not a valid player name.");
|
||||||
|
if (!ByteString.FromString(parts[0], out var p, false))
|
||||||
|
throw new IdentifierParseError($"The player string {parts[0]} contains invalid symbols.");
|
||||||
|
|
||||||
|
var world = parts.Length == 2
|
||||||
|
? Data.ToWorldId(parts[1])
|
||||||
|
: ushort.MaxValue;
|
||||||
|
|
||||||
|
if (!VerifyWorld(world))
|
||||||
|
throw new IdentifierParseError($"{parts[1]} is not a valid world name.");
|
||||||
|
|
||||||
|
return (p, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
(ObjectKind, uint) ParseNpc(string npc)
|
||||||
|
{
|
||||||
|
var split = npc.Split(':', 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (split.Length != 2)
|
||||||
|
throw new IdentifierParseError("NPCs need to be specified by '[Object Type]:[NPC Name]'.");
|
||||||
|
|
||||||
|
static bool FindDataId(string name, IReadOnlyDictionary<uint, string> data, out uint dataId)
|
||||||
|
{
|
||||||
|
var kvp = data.FirstOrDefault(kvp => kvp.Value.Equals(name, StringComparison.OrdinalIgnoreCase),
|
||||||
|
new KeyValuePair<uint, string>(uint.MaxValue, string.Empty));
|
||||||
|
dataId = kvp.Key;
|
||||||
|
return kvp.Value.Length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (split[0].ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "m":
|
||||||
|
case "mount":
|
||||||
|
return FindDataId(split[1], Data.Mounts, out var id)
|
||||||
|
? (ObjectKind.MountType, id)
|
||||||
|
: throw new IdentifierParseError($"Could not identify a Mount named {split[1]}.");
|
||||||
|
case "c":
|
||||||
|
case "companion":
|
||||||
|
case "minion":
|
||||||
|
case "mini":
|
||||||
|
return FindDataId(split[1], Data.Companions, out id)
|
||||||
|
? (ObjectKind.Companion, id)
|
||||||
|
: throw new IdentifierParseError($"Could not identify a Minion named {split[1]}.");
|
||||||
|
case "a":
|
||||||
|
case "o":
|
||||||
|
case "accessory":
|
||||||
|
case "ornament":
|
||||||
|
// TODO: Objectkind ornament.
|
||||||
|
return FindDataId(split[1], Data.Ornaments, out id)
|
||||||
|
? ((ObjectKind)15, id)
|
||||||
|
: throw new IdentifierParseError($"Could not identify an Accessory named {split[1]}.");
|
||||||
|
case "e":
|
||||||
|
case "enpc":
|
||||||
|
case "eventnpc":
|
||||||
|
case "event npc":
|
||||||
|
return FindDataId(split[1], Data.ENpcs, out id)
|
||||||
|
? (ObjectKind.EventNpc, id)
|
||||||
|
: throw new IdentifierParseError($"Could not identify an Event NPC named {split[1]}.");
|
||||||
|
case "b":
|
||||||
|
case "bnpc":
|
||||||
|
case "battlenpc":
|
||||||
|
case "battle npc":
|
||||||
|
return FindDataId(split[1], Data.BNpcs, out id)
|
||||||
|
? (ObjectKind.BattleNpc, id)
|
||||||
|
: throw new IdentifierParseError($"Could not identify a Battle NPC named {split[1]}.");
|
||||||
|
default:
|
||||||
|
throw new IdentifierParseError($"The argument {split[0]} is not a valid NPC Type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (split[0].ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "p":
|
||||||
|
case "player":
|
||||||
|
type = IdentifierType.Player;
|
||||||
|
(playerName, worldId) = ParsePlayer(split[1]);
|
||||||
|
break;
|
||||||
|
case "r":
|
||||||
|
case "retainer":
|
||||||
|
type = IdentifierType.Retainer;
|
||||||
|
if (!VerifyRetainerName(split[1]))
|
||||||
|
throw new IdentifierParseError($"{split[1]} is not a valid player name.");
|
||||||
|
if (!ByteString.FromString(split[1], out playerName, false))
|
||||||
|
throw new IdentifierParseError($"The retainer string {split[1]} contains invalid symbols.");
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "n":
|
||||||
|
case "npc":
|
||||||
|
type = IdentifierType.Npc;
|
||||||
|
(kind, objectId) = ParseNpc(split[1]);
|
||||||
|
break;
|
||||||
|
case "o":
|
||||||
|
case "owned":
|
||||||
|
if (split.Length < 3)
|
||||||
|
throw new IdentifierParseError(
|
||||||
|
"Owned NPCs need a NPC and a player, separated by '|', but only one was provided.");
|
||||||
|
type = IdentifierType.Owned;
|
||||||
|
(kind, objectId) = ParseNpc(split[1]);
|
||||||
|
(playerName, worldId) = ParsePlayer(split[2]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IdentifierParseError(
|
||||||
|
$"{split[0]} is not a valid identifier type. Valid types are [P]layer, [R]etainer, [N]PC, or [O]wned");
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateIndividualUnchecked(type, playerName, worldId, kind, objectId);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compute an ActorIdentifier from a GameObject. If check is true, the values are checked for validity.
|
/// Compute an ActorIdentifier from a GameObject. If check is true, the values are checked for validity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -154,7 +288,7 @@ public partial class ActorManager
|
||||||
// Hack to support Anamnesis changing ObjectKind for NPC faces.
|
// Hack to support Anamnesis changing ObjectKind for NPC faces.
|
||||||
if (nameId == 0 && allowPlayerNpc)
|
if (nameId == 0 && allowPlayerNpc)
|
||||||
{
|
{
|
||||||
var name = new ByteString(actor->Name);
|
var name = new ByteString(actor->Name);
|
||||||
if (!name.IsEmpty)
|
if (!name.IsEmpty)
|
||||||
{
|
{
|
||||||
var homeWorld = ((Character*)actor)->HomeWorld;
|
var homeWorld = ((Character*)actor)->HomeWorld;
|
||||||
|
|
@ -244,7 +378,8 @@ public partial class ActorManager
|
||||||
|
|
||||||
public unsafe ActorIdentifier FromObject(GameObject? actor, out FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner,
|
public unsafe ActorIdentifier FromObject(GameObject? actor, out FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner,
|
||||||
bool allowPlayerNpc, bool check)
|
bool allowPlayerNpc, bool check)
|
||||||
=> FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(actor?.Address ?? IntPtr.Zero), out owner, allowPlayerNpc, check);
|
=> FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(actor?.Address ?? IntPtr.Zero), out owner, allowPlayerNpc,
|
||||||
|
check);
|
||||||
|
|
||||||
public unsafe ActorIdentifier FromObject(GameObject? actor, bool allowPlayerNpc, bool check)
|
public unsafe ActorIdentifier FromObject(GameObject? actor, bool allowPlayerNpc, bool check)
|
||||||
=> FromObject(actor, out _, allowPlayerNpc, check);
|
=> FromObject(actor, out _, allowPlayerNpc, check);
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ public partial class ModCollection
|
||||||
CollectionType.Default => Default,
|
CollectionType.Default => Default,
|
||||||
CollectionType.Interface => Interface,
|
CollectionType.Interface => Interface,
|
||||||
CollectionType.Current => Current,
|
CollectionType.Current => Current,
|
||||||
CollectionType.Individual => identifier.IsValid ? Individuals.TryGetCollection( identifier, out var c ) ? c : null : null,
|
CollectionType.Individual => identifier.IsValid && Individuals.Individuals.TryGetValue( identifier, out var c ) ? c : null,
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -213,6 +213,50 @@ public static class CollectionTypeExtensions
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryParse( string text, out CollectionType type )
|
||||||
|
{
|
||||||
|
if( Enum.TryParse( text, true, out type ) )
|
||||||
|
return type is not CollectionType.Inactive and not CollectionType.Temporary;
|
||||||
|
|
||||||
|
if( string.Equals( text, "character", StringComparison.OrdinalIgnoreCase ) )
|
||||||
|
{
|
||||||
|
type = CollectionType.Individual;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( string.Equals( text, "base", StringComparison.OrdinalIgnoreCase ) )
|
||||||
|
{
|
||||||
|
type = CollectionType.Default;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( string.Equals( text, "ui", StringComparison.OrdinalIgnoreCase ) )
|
||||||
|
{
|
||||||
|
type = CollectionType.Interface;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( string.Equals( text, "selected", StringComparison.OrdinalIgnoreCase ) )
|
||||||
|
{
|
||||||
|
type = CollectionType.Current;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach( var t in Enum.GetValues< CollectionType >() )
|
||||||
|
{
|
||||||
|
if( t is CollectionType.Inactive or CollectionType.Temporary )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( string.Equals( text, t.ToName(), StringComparison.OrdinalIgnoreCase ) )
|
||||||
|
{
|
||||||
|
type = t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static string ToName( this CollectionType collectionType )
|
public static string ToName( this CollectionType collectionType )
|
||||||
=> collectionType switch
|
=> collectionType switch
|
||||||
{
|
{
|
||||||
|
|
@ -288,7 +332,7 @@ public static class CollectionTypeExtensions
|
||||||
CollectionType.Inactive => "Collection",
|
CollectionType.Inactive => "Collection",
|
||||||
CollectionType.Default => "Default",
|
CollectionType.Default => "Default",
|
||||||
CollectionType.Interface => "Interface",
|
CollectionType.Interface => "Interface",
|
||||||
CollectionType.Individual => "Character",
|
CollectionType.Individual => "Individual",
|
||||||
CollectionType.Current => "Current",
|
CollectionType.Current => "Current",
|
||||||
_ => string.Empty,
|
_ => string.Empty,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -177,10 +177,10 @@ public sealed partial class IndividualCollections
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool Delete( ActorIdentifier identifier )
|
internal bool Delete( ActorIdentifier identifier )
|
||||||
=> Delete( DisplayString( identifier ) );
|
=> Delete( Index( identifier ) );
|
||||||
|
|
||||||
internal bool Delete( string displayName )
|
internal bool Delete( string displayName )
|
||||||
=> Delete( _assignments.FindIndex( t => t.DisplayName.Equals( displayName, StringComparison.OrdinalIgnoreCase ) ) );
|
=> Delete( Index( displayName ) );
|
||||||
|
|
||||||
internal bool Delete( int displayIndex )
|
internal bool Delete( int displayIndex )
|
||||||
{
|
{
|
||||||
|
|
@ -202,6 +202,12 @@ public sealed partial class IndividualCollections
|
||||||
internal bool Move( int from, int to )
|
internal bool Move( int from, int to )
|
||||||
=> _assignments.Move( from, to );
|
=> _assignments.Move( from, to );
|
||||||
|
|
||||||
|
internal int Index( string displayName )
|
||||||
|
=> _assignments.FindIndex( t => t.DisplayName.Equals( displayName, StringComparison.OrdinalIgnoreCase ) );
|
||||||
|
|
||||||
|
internal int Index( ActorIdentifier identifier )
|
||||||
|
=> identifier.IsValid ? Index( DisplayString( identifier ) ) : -1;
|
||||||
|
|
||||||
private string DisplayString( ActorIdentifier identifier )
|
private string DisplayString( ActorIdentifier identifier )
|
||||||
{
|
{
|
||||||
return identifier.Type switch
|
return identifier.Type switch
|
||||||
|
|
|
||||||
474
Penumbra/CommandHandler.cs
Normal file
474
Penumbra/CommandHandler.cs
Normal file
|
|
@ -0,0 +1,474 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Dalamud.Game.Command;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using ImGuiNET;
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
using Penumbra.Interop;
|
||||||
|
using Penumbra.Mods;
|
||||||
|
using Penumbra.UI;
|
||||||
|
|
||||||
|
namespace Penumbra;
|
||||||
|
|
||||||
|
public static class SeStringBuilderExtensions
|
||||||
|
{
|
||||||
|
public const ushort Green = 504;
|
||||||
|
public const ushort Yellow = 31;
|
||||||
|
public const ushort Red = 534;
|
||||||
|
public const ushort Blue = 517;
|
||||||
|
public const ushort White = 1;
|
||||||
|
public const ushort Purple = 541;
|
||||||
|
|
||||||
|
public static SeStringBuilder AddText( this SeStringBuilder sb, string text, int color, bool brackets = false )
|
||||||
|
=> sb.AddUiForeground( ( ushort )color ).AddText( brackets ? $"[{text}]" : text ).AddUiForegroundOff();
|
||||||
|
|
||||||
|
public static SeStringBuilder AddGreen( this SeStringBuilder sb, string text, bool brackets = false )
|
||||||
|
=> AddText( sb, text, Green, brackets );
|
||||||
|
|
||||||
|
public static SeStringBuilder AddYellow( this SeStringBuilder sb, string text, bool brackets = false )
|
||||||
|
=> AddText( sb, text, Yellow, brackets );
|
||||||
|
|
||||||
|
public static SeStringBuilder AddRed( this SeStringBuilder sb, string text, bool brackets = false )
|
||||||
|
=> AddText( sb, text, Red, brackets );
|
||||||
|
|
||||||
|
public static SeStringBuilder AddBlue( this SeStringBuilder sb, string text, bool brackets = false )
|
||||||
|
=> AddText( sb, text, Blue, brackets );
|
||||||
|
|
||||||
|
public static SeStringBuilder AddWhite( this SeStringBuilder sb, string text, bool brackets = false )
|
||||||
|
=> AddText( sb, text, White, brackets );
|
||||||
|
|
||||||
|
public static SeStringBuilder AddPurple( this SeStringBuilder sb, string text, bool brackets = false )
|
||||||
|
=> AddText( sb, text, Purple, brackets );
|
||||||
|
|
||||||
|
public static SeStringBuilder AddCommand( this SeStringBuilder sb, string command, string description )
|
||||||
|
=> sb.AddText( " 》 " )
|
||||||
|
.AddBlue( command )
|
||||||
|
.AddText( $" - {description}" );
|
||||||
|
|
||||||
|
public static SeStringBuilder AddInitialPurple( this SeStringBuilder sb, string word, bool withComma = true )
|
||||||
|
=> sb.AddPurple( $"[{word[ 0 ]}]" )
|
||||||
|
.AddText( withComma ? $"{word[ 1.. ]}, " : word[ 1.. ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CommandHandler : IDisposable
|
||||||
|
{
|
||||||
|
private const string CommandName = "/penumbra";
|
||||||
|
|
||||||
|
private readonly CommandManager _commandManager;
|
||||||
|
private readonly ObjectReloader _objectReloader;
|
||||||
|
private readonly Configuration _config;
|
||||||
|
private readonly Penumbra _penumbra;
|
||||||
|
private readonly ConfigWindow _configWindow;
|
||||||
|
private readonly ActorManager _actors;
|
||||||
|
private readonly Mod.Manager _modManager;
|
||||||
|
private readonly ModCollection.Manager _collectionManager;
|
||||||
|
|
||||||
|
public CommandHandler( CommandManager commandManager, ObjectReloader objectReloader, Configuration config, Penumbra penumbra, ConfigWindow configWindow, Mod.Manager modManager,
|
||||||
|
ModCollection.Manager collectionManager, ActorManager actors )
|
||||||
|
{
|
||||||
|
_commandManager = commandManager;
|
||||||
|
_objectReloader = objectReloader;
|
||||||
|
_config = config;
|
||||||
|
_penumbra = penumbra;
|
||||||
|
_configWindow = configWindow;
|
||||||
|
_modManager = modManager;
|
||||||
|
_collectionManager = collectionManager;
|
||||||
|
_actors = actors;
|
||||||
|
_commandManager.AddHandler( CommandName, new CommandInfo( OnCommand ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_commandManager.RemoveHandler( CommandName );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCommand( string command, string arguments )
|
||||||
|
{
|
||||||
|
if( arguments.Length == 0 )
|
||||||
|
{
|
||||||
|
arguments = "window";
|
||||||
|
}
|
||||||
|
|
||||||
|
var argumentList = arguments.Split( ' ', 2 );
|
||||||
|
arguments = argumentList.Length == 2 ? argumentList[ 1 ] : string.Empty;
|
||||||
|
|
||||||
|
var _ = argumentList[ 0 ].ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"window" => ToggleWindow( arguments ),
|
||||||
|
"enable" => SetPenumbraState( arguments, true ),
|
||||||
|
"disable" => SetPenumbraState( arguments, false ),
|
||||||
|
"toggle" => SetPenumbraState( arguments, null ),
|
||||||
|
"reload" => Reload( arguments ),
|
||||||
|
"redraw" => Redraw( arguments ),
|
||||||
|
"lockui" => SetUiLockState( arguments ),
|
||||||
|
"debug" => SetDebug( arguments ),
|
||||||
|
"collection" => SetCollection( arguments ),
|
||||||
|
"mod" => SetMod( arguments ),
|
||||||
|
_ => PrintHelp( argumentList[ 0 ] ),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool PrintHelp( string arguments )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "The given argument " ).AddRed( arguments, true ).AddText( " is not valid. Valid arguments are:" ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "window",
|
||||||
|
"Toggle the Penumbra main config window. Can be used with [on|off] to force specific state. Also used when no argument is provided." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "enable", "Enable modding and force a redraw of all game objects if it was previously disabled." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "disable", "Disable modding and force a redraw of all game objects if it was previously enabled." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "toggle", "Toggle modding and force a redraw of all game objects." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "reload", "Rediscover the mod directory and reload all mods." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "redraw", "Redraw all game objects. Specify a placeholder or a name to redraw specific objects." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "lockui", "Toggle the locked state of the main Penumbra window. Can be used with [on|off] to force specific state." )
|
||||||
|
.BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "debug", "Toggle debug mode for Penumbra. Can be used with [on|off] to force specific state." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "collection", "Change your active collection setup. Use without further parameters for more detailed help." )
|
||||||
|
.BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddCommand( "mod", "Change a specific mods settings. Use without further parameters for more detailed help." ).BuiltString );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ToggleWindow( string arguments )
|
||||||
|
{
|
||||||
|
var value = ParseTrueFalseToggle( arguments ) ?? !_configWindow.IsOpen;
|
||||||
|
if( value == _configWindow.IsOpen )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_configWindow.Toggle();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Reload( string _ )
|
||||||
|
{
|
||||||
|
_modManager.DiscoverMods();
|
||||||
|
Dalamud.Chat.Print( $"Reloaded Penumbra mods. You have {_modManager.Count} mods." );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Redraw( string arguments )
|
||||||
|
{
|
||||||
|
if( arguments.Length > 0 )
|
||||||
|
{
|
||||||
|
_objectReloader.RedrawObject( arguments, RedrawType.Redraw );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_objectReloader.RedrawAll( RedrawType.Redraw );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetDebug( string arguments )
|
||||||
|
{
|
||||||
|
var value = ParseTrueFalseToggle( arguments ) ?? !_config.DebugMode;
|
||||||
|
if( value == _config.DebugMode )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dalamud.Chat.Print( value
|
||||||
|
? "Debug mode enabled."
|
||||||
|
: "Debug mode disabled." );
|
||||||
|
|
||||||
|
_config.DebugMode = value;
|
||||||
|
_config.Save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetPenumbraState( string _, bool? newValue )
|
||||||
|
{
|
||||||
|
var value = newValue ?? !_config.EnableMods;
|
||||||
|
|
||||||
|
if( value == _config.EnableMods )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( value
|
||||||
|
? "Your mods are already enabled. To disable your mods, please run the following command instead: /penumbra disable"
|
||||||
|
: "Your mods are already disabled. To enable your mods, please run the following command instead: /penumbra enable" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dalamud.Chat.Print( value
|
||||||
|
? "Your mods have been enabled."
|
||||||
|
: "Your mods have been disabled." );
|
||||||
|
return _penumbra.SetEnabled( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetUiLockState( string arguments )
|
||||||
|
{
|
||||||
|
var value = ParseTrueFalseToggle( arguments ) ?? !_config.FixMainWindow;
|
||||||
|
if( value == _config.FixMainWindow )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( value )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( "Penumbra UI locked in place." );
|
||||||
|
_configWindow.Flags |= ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( "Penumbra UI unlocked." );
|
||||||
|
_configWindow.Flags &= ~( ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize );
|
||||||
|
}
|
||||||
|
|
||||||
|
_config.FixMainWindow = value;
|
||||||
|
_config.Save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetCollection( string arguments )
|
||||||
|
{
|
||||||
|
if( arguments.Length == 0 )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "Use with /penumbra collection " ).AddBlue( "[Collection Type]" ).AddText( " | " ).AddYellow( "[Collection Name]" )
|
||||||
|
.AddText( " | " ).AddGreen( "<Identifier>" ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( " 》 Valid Collection Types are " ).AddBlue( "Base" ).AddText( ", " ).AddBlue( "Ui" ).AddText( ", " )
|
||||||
|
.AddBlue( "Selected" ).AddText( ", " )
|
||||||
|
.AddBlue( "Individual" ).AddText( ", and all those selectable in Character Groups." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( " 》 Valid Collection Names are " ).AddYellow( "None" )
|
||||||
|
.AddText( ", all collections you have created by their full names, and " ).AddYellow( "Delete" ).AddText( " to remove assignments (not valid for all types)." )
|
||||||
|
.BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( " 》 If the type is " ).AddBlue( "Individual" )
|
||||||
|
.AddText( " you need to specify an individual with an identifier of the form:" ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( " 》》》 " ).AddGreen( "p" ).AddText( " | " ).AddWhite( "[Player Name]@<World Name>" )
|
||||||
|
.AddText( ", if no @ is provided, Any World is used." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( " 》》》 " ).AddGreen( "r" ).AddText( " | " ).AddWhite( "[Retainer Name]" ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( " 》》》 " ).AddGreen( "n" ).AddText( " | " ).AddPurple( "[NPC Type]" ).AddText( " : " )
|
||||||
|
.AddRed( "[NPC Name]" ).AddText( ", where NPC Type can be " ).AddInitialPurple( "Mount" ).AddInitialPurple( "Companion" ).AddInitialPurple( "Accessory" )
|
||||||
|
.AddInitialPurple( "Event NPC" ).AddText( "or " )
|
||||||
|
.AddInitialPurple( "Battle NPC", false ).AddText( "." ).BuiltString );
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( " 》》》 " ).AddGreen( "o" ).AddText( " | " ).AddPurple( "[NPC Type]" ).AddText( " : " )
|
||||||
|
.AddRed( "[NPC Name]" ).AddText( " | " ).AddWhite( "[Player Name]@<World Name>" ).AddText( "." ).BuiltString );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var split = arguments.Split( '|', 3, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries );
|
||||||
|
var typeName = split[ 0 ];
|
||||||
|
|
||||||
|
if( !CollectionTypeExtensions.TryParse( typeName, out var type ) )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "The argument " ).AddRed( typeName, true ).AddText( " is not a valid collection type." ).BuiltString );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( split.Length == 1 )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( "There was no collection name provided." );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !GetModCollection( split[ 1 ], out var collection ) )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var identifier = ActorIdentifier.Invalid;
|
||||||
|
if( type is CollectionType.Individual )
|
||||||
|
{
|
||||||
|
if( split.Length == 2 )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( "Setting an individual collection requires a collection name and an identifier, but no identifier was provided." );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
identifier = _actors.FromUserString( split[ 2 ] );
|
||||||
|
}
|
||||||
|
catch( ActorManager.IdentifierParseError e )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "The argument " ).AddRed( split[ 2 ], true ).AddText( $" could not be converted to an identifier. {e.Message}" )
|
||||||
|
.BuiltString );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldCollection = _collectionManager.ByType( type, identifier );
|
||||||
|
if( collection == oldCollection )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( collection == null
|
||||||
|
? $"The {type.ToName()} Collection{( identifier.IsValid ? $" for {identifier}" : string.Empty )} is already unassigned"
|
||||||
|
: $"{collection.Name} already is the {type.ToName()} Collection{( identifier.IsValid ? $" for {identifier}." : "." )}" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var individualIndex = _collectionManager.Individuals.Index( identifier );
|
||||||
|
|
||||||
|
if( oldCollection == null )
|
||||||
|
{
|
||||||
|
if( type.IsSpecial() )
|
||||||
|
{
|
||||||
|
_collectionManager.CreateSpecialCollection( type );
|
||||||
|
}
|
||||||
|
else if( identifier.IsValid )
|
||||||
|
{
|
||||||
|
var identifiers = _collectionManager.Individuals.GetGroup( identifier );
|
||||||
|
individualIndex = _collectionManager.Individuals.Count;
|
||||||
|
_collectionManager.CreateIndividualCollection( identifiers );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( collection == null )
|
||||||
|
{
|
||||||
|
if( type.IsSpecial() )
|
||||||
|
{
|
||||||
|
_collectionManager.RemoveSpecialCollection( type );
|
||||||
|
}
|
||||||
|
else if( individualIndex >= 0 )
|
||||||
|
{
|
||||||
|
_collectionManager.RemoveIndividualCollection( individualIndex );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( $"Can not remove the {type.ToName()} Collection assignment {( identifier.IsValid ? $" for {identifier}." : "." )}" );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dalamud.Chat.Print( $"Removed {oldCollection.Name} as {type.ToName()} Collection assignment {( identifier.IsValid ? $" for {identifier}." : "." )}" );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_collectionManager.SetCollection( collection!, type, individualIndex );
|
||||||
|
Dalamud.Chat.Print( $"Assigned {collection!.Name} as {type.ToName()} Collection{( identifier.IsValid ? $" for {identifier}." : "." )}" );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SetMod( string arguments )
|
||||||
|
{
|
||||||
|
if( arguments.Length == 0 )
|
||||||
|
{
|
||||||
|
var seString = new SeStringBuilder()
|
||||||
|
.AddText( "Use with /penumbra mod " ).AddBlue( "[enable|disable|inherit|toggle]" ).AddYellow( "[Collection Name]" ).AddText( " | " )
|
||||||
|
.AddPurple( "[Mod Name or Mod Directory Name]" );
|
||||||
|
Dalamud.Chat.Print( seString.BuiltString );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var split = arguments.Split( ' ', 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries );
|
||||||
|
var nameSplit = split.Length != 2 ? Array.Empty< string >() : split[ 1 ].Split( '|', 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries );
|
||||||
|
if( nameSplit.Length != 2 )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( "Not enough arguments provided." );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = split[ 0 ].ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"enable" => 0,
|
||||||
|
"enabled" => 0,
|
||||||
|
"disable" => 1,
|
||||||
|
"disabled" => 1,
|
||||||
|
"toggle" => 2,
|
||||||
|
"inherit" => 3,
|
||||||
|
"inherited" => 3,
|
||||||
|
_ => -1,
|
||||||
|
};
|
||||||
|
if( state == -1 )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddRed( split[ 0 ], true ).AddText( " is not a valid type of setting." ).BuiltString );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !GetModCollection( nameSplit[ 0 ], out var collection ) || collection == ModCollection.Empty )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !_modManager.TryGetMod( nameSplit[ 1 ], nameSplit[ 1 ], out var mod ) )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "The mod " ).AddRed( nameSplit[ 1 ], true ).AddText( " does not exist." ).BuiltString );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings = collection.Settings[ mod.Index ];
|
||||||
|
switch( state )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if( collection.SetModState( mod.Index, true ) )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "Enabled mod " ).AddPurple( mod.Name, true ).AddText( " in collection " ).AddYellow( collection.Name, true )
|
||||||
|
.AddText( "." ).BuiltString );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if( collection.SetModState( mod.Index, false ) )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "Disabled mod " ).AddPurple( mod.Name, true ).AddText( " in collection " ).AddYellow( collection.Name, true )
|
||||||
|
.AddText( "." ).BuiltString );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
var setting = !( settings?.Enabled ?? false );
|
||||||
|
if( collection.SetModState( mod.Index, setting ) )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( setting ? "Enabled mod " : "Disabled mod " ).AddPurple( mod.Name, true ).AddText( " in collection " )
|
||||||
|
.AddYellow( collection.Name, true )
|
||||||
|
.AddText( "." ).BuiltString );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if( collection.SetModInheritance( mod.Index, true ) )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "Set mod " ).AddPurple( mod.Name, true ).AddText( " in collection " ).AddYellow( collection.Name, true )
|
||||||
|
.AddText( " to inherit." ).BuiltString );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "Mod " ).AddPurple( mod.Name, true ).AddText( "already had the desired state in collection " )
|
||||||
|
.AddYellow( collection.Name, true ).AddText( "." ).BuiltString );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetModCollection( string collectionName, out ModCollection? collection )
|
||||||
|
{
|
||||||
|
var lowerName = collectionName.ToLowerInvariant();
|
||||||
|
if( lowerName == "delete" )
|
||||||
|
{
|
||||||
|
collection = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
collection = string.Equals( lowerName, ModCollection.Empty.Name, StringComparison.OrdinalIgnoreCase )
|
||||||
|
? ModCollection.Empty
|
||||||
|
: _collectionManager[ lowerName ];
|
||||||
|
if( collection == null )
|
||||||
|
{
|
||||||
|
Dalamud.Chat.Print( new SeStringBuilder().AddText( "The collection " ).AddRed( collectionName, true ).AddText( " does not exist." ).BuiltString );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool? ParseTrueFalseToggle( string value )
|
||||||
|
=> value.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"0" => false,
|
||||||
|
"false" => false,
|
||||||
|
"off" => false,
|
||||||
|
"disable" => false,
|
||||||
|
"disabled" => false,
|
||||||
|
|
||||||
|
"1" => true,
|
||||||
|
"true" => true,
|
||||||
|
"on" => true,
|
||||||
|
"enable" => true,
|
||||||
|
"enabled" => true,
|
||||||
|
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,6 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Dalamud.Game.Command;
|
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using EmbedIO;
|
using EmbedIO;
|
||||||
|
|
@ -26,7 +25,6 @@ using Penumbra.GameData.Actors;
|
||||||
using Penumbra.Interop.Loader;
|
using Penumbra.Interop.Loader;
|
||||||
using Penumbra.Interop.Resolver;
|
using Penumbra.Interop.Resolver;
|
||||||
using Penumbra.Mods;
|
using Penumbra.Mods;
|
||||||
using Penumbra.String;
|
|
||||||
using CharacterUtility = Penumbra.Interop.CharacterUtility;
|
using CharacterUtility = Penumbra.Interop.CharacterUtility;
|
||||||
using ResidentResourceManager = Penumbra.Interop.ResidentResourceManager;
|
using ResidentResourceManager = Penumbra.Interop.ResidentResourceManager;
|
||||||
|
|
||||||
|
|
@ -41,8 +39,6 @@ public class Penumbra : IDalamudPlugin
|
||||||
public string Name
|
public string Name
|
||||||
=> "Penumbra";
|
=> "Penumbra";
|
||||||
|
|
||||||
private const string CommandName = "/penumbra";
|
|
||||||
|
|
||||||
public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? string.Empty;
|
public static readonly string Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? string.Empty;
|
||||||
|
|
||||||
public static readonly string CommitHash =
|
public static readonly string CommitHash =
|
||||||
|
|
@ -80,6 +76,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
private readonly LaunchButton _launchButton;
|
private readonly LaunchButton _launchButton;
|
||||||
private readonly WindowSystem _windowSystem;
|
private readonly WindowSystem _windowSystem;
|
||||||
private readonly Changelog _changelog;
|
private readonly Changelog _changelog;
|
||||||
|
private readonly CommandHandler _commandHandler;
|
||||||
|
|
||||||
internal WebServer? WebServer;
|
internal WebServer? WebServer;
|
||||||
|
|
||||||
|
|
@ -116,12 +113,8 @@ public class Penumbra : IDalamudPlugin
|
||||||
ObjectReloader = new ObjectReloader();
|
ObjectReloader = new ObjectReloader();
|
||||||
PathResolver = new PathResolver( ResourceLoader );
|
PathResolver = new PathResolver( ResourceLoader );
|
||||||
|
|
||||||
Dalamud.Commands.AddHandler( CommandName, new CommandInfo( OnCommand )
|
|
||||||
{
|
|
||||||
HelpMessage = "/penumbra - toggle ui\n/penumbra reload - reload mod file lists & discover any new mods",
|
|
||||||
} );
|
|
||||||
|
|
||||||
SetupInterface( out _configWindow, out _launchButton, out _windowSystem, out _changelog );
|
SetupInterface( out _configWindow, out _launchButton, out _windowSystem, out _changelog );
|
||||||
|
_commandHandler = new CommandHandler( Dalamud.Commands, ObjectReloader, Config, this, _configWindow, ModManager, CollectionManager, Actors );
|
||||||
|
|
||||||
if( Config.EnableMods )
|
if( Config.EnableMods )
|
||||||
{
|
{
|
||||||
|
|
@ -203,54 +196,42 @@ public class Penumbra : IDalamudPlugin
|
||||||
|
|
||||||
public event Action< bool >? EnabledChange;
|
public event Action< bool >? EnabledChange;
|
||||||
|
|
||||||
public bool Enable()
|
|
||||||
{
|
|
||||||
if( Config.EnableMods )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.EnableMods = true;
|
|
||||||
ResourceLoader.EnableReplacements();
|
|
||||||
PathResolver.Enable();
|
|
||||||
Config.Save();
|
|
||||||
if( CharacterUtility.Ready )
|
|
||||||
{
|
|
||||||
CollectionManager.Default.SetFiles();
|
|
||||||
ResidentResources.Reload();
|
|
||||||
ObjectReloader.RedrawAll( RedrawType.Redraw );
|
|
||||||
}
|
|
||||||
|
|
||||||
EnabledChange?.Invoke( true );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Disable()
|
|
||||||
{
|
|
||||||
if( !Config.EnableMods )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.EnableMods = false;
|
|
||||||
ResourceLoader.DisableReplacements();
|
|
||||||
PathResolver.Disable();
|
|
||||||
Config.Save();
|
|
||||||
if( CharacterUtility.Ready )
|
|
||||||
{
|
|
||||||
CharacterUtility.ResetAll();
|
|
||||||
ResidentResources.Reload();
|
|
||||||
ObjectReloader.RedrawAll( RedrawType.Redraw );
|
|
||||||
}
|
|
||||||
|
|
||||||
EnabledChange?.Invoke( false );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetEnabled( bool enabled )
|
public bool SetEnabled( bool enabled )
|
||||||
=> enabled ? Enable() : Disable();
|
{
|
||||||
|
if( enabled == Config.EnableMods )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.EnableMods = enabled;
|
||||||
|
if( enabled )
|
||||||
|
{
|
||||||
|
ResourceLoader.EnableReplacements();
|
||||||
|
PathResolver.Enable();
|
||||||
|
if( CharacterUtility.Ready )
|
||||||
|
{
|
||||||
|
CollectionManager.Default.SetFiles();
|
||||||
|
ResidentResources.Reload();
|
||||||
|
ObjectReloader.RedrawAll( RedrawType.Redraw );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResourceLoader.DisableReplacements();
|
||||||
|
PathResolver.Disable();
|
||||||
|
if( CharacterUtility.Ready )
|
||||||
|
{
|
||||||
|
CharacterUtility.ResetAll();
|
||||||
|
ResidentResources.Reload();
|
||||||
|
ObjectReloader.RedrawAll( RedrawType.Redraw );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Config.Save();
|
||||||
|
EnabledChange?.Invoke( enabled );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void ForceChangelogOpen()
|
public void ForceChangelogOpen()
|
||||||
=> _changelog.ForceOpen = true;
|
=> _changelog.ForceOpen = true;
|
||||||
|
|
@ -303,181 +284,24 @@ public class Penumbra : IDalamudPlugin
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
ShutdownWebServer();
|
||||||
|
IpcProviders?.Dispose();
|
||||||
|
Api?.Dispose();
|
||||||
|
_commandHandler?.Dispose();
|
||||||
StainManager?.Dispose();
|
StainManager?.Dispose();
|
||||||
Actors?.Dispose();
|
Actors?.Dispose();
|
||||||
Identifier?.Dispose();
|
Identifier?.Dispose();
|
||||||
Framework?.Dispose();
|
Framework?.Dispose();
|
||||||
ShutdownWebServer();
|
|
||||||
DisposeInterface();
|
DisposeInterface();
|
||||||
IpcProviders?.Dispose();
|
|
||||||
Api?.Dispose();
|
|
||||||
ObjectReloader?.Dispose();
|
ObjectReloader?.Dispose();
|
||||||
ModFileSystem?.Dispose();
|
ModFileSystem?.Dispose();
|
||||||
CollectionManager?.Dispose();
|
CollectionManager?.Dispose();
|
||||||
|
|
||||||
Dalamud.Commands.RemoveHandler( CommandName );
|
|
||||||
|
|
||||||
PathResolver?.Dispose();
|
PathResolver?.Dispose();
|
||||||
ResourceLogger?.Dispose();
|
ResourceLogger?.Dispose();
|
||||||
ResourceLoader?.Dispose();
|
ResourceLoader?.Dispose();
|
||||||
CharacterUtility?.Dispose();
|
CharacterUtility?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool SetCollection( string typeName, string collectionName )
|
|
||||||
{
|
|
||||||
if( !Enum.TryParse< CollectionType >( typeName, true, out var type ) || type == CollectionType.Inactive )
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print(
|
|
||||||
"Second command argument is not a valid collection type, the correct command format is: /penumbra collection <collectionType> <collectionName> [| characterName]" );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
string? characterName = null;
|
|
||||||
var identifier = ActorIdentifier.Invalid;
|
|
||||||
if( type is CollectionType.Individual )
|
|
||||||
{
|
|
||||||
var split = collectionName.Split( '|', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries );
|
|
||||||
if( split.Length < 2 || split[ 0 ].Length == 0 || split[ 1 ].Length == 0 )
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print( "You need to provide a collection and a character name in the form of 'collection | name' to set an individual collection." );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
collectionName = split[ 0 ];
|
|
||||||
characterName = split[ 1 ];
|
|
||||||
|
|
||||||
identifier = Actors.CreatePlayer( ByteString.FromStringUnsafe( characterName, false ), ushort.MaxValue );
|
|
||||||
if( !identifier.IsValid )
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print( $"{characterName} is not a valid character name." );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
collectionName = collectionName.ToLowerInvariant();
|
|
||||||
var collection = string.Equals( collectionName, ModCollection.Empty.Name, StringComparison.OrdinalIgnoreCase )
|
|
||||||
? ModCollection.Empty
|
|
||||||
: CollectionManager[ collectionName ];
|
|
||||||
if( collection == null )
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print( $"The collection {collection} does not exist." );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldCollection = CollectionManager.ByType( type, identifier );
|
|
||||||
if( collection == oldCollection )
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print( $"{collection.Name} already is the {type.ToName()} Collection." );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( oldCollection == null )
|
|
||||||
{
|
|
||||||
if( type.IsSpecial() )
|
|
||||||
{
|
|
||||||
CollectionManager.CreateSpecialCollection( type );
|
|
||||||
}
|
|
||||||
else if( type is CollectionType.Individual )
|
|
||||||
{
|
|
||||||
CollectionManager.CreateIndividualCollection( identifier );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CollectionManager.SetCollection( collection, type, CollectionManager.Individuals.Count - 1 );
|
|
||||||
Dalamud.Chat.Print( $"Set {collection.Name} as {type.ToName()} Collection{( characterName != null ? $" for {characterName}." : "." )}" );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCommand( string command, string rawArgs )
|
|
||||||
{
|
|
||||||
const string modsEnabled = "Your mods have now been enabled.";
|
|
||||||
const string modsDisabled = "Your mods have now been disabled.";
|
|
||||||
|
|
||||||
var args = rawArgs.Split( new[] { ' ' }, 2 );
|
|
||||||
if( args.Length > 0 && args[ 0 ].Length > 0 )
|
|
||||||
{
|
|
||||||
switch( args[ 0 ] )
|
|
||||||
{
|
|
||||||
case "reload":
|
|
||||||
{
|
|
||||||
ModManager.DiscoverMods();
|
|
||||||
Dalamud.Chat.Print( $"Reloaded Penumbra mods. You have {ModManager.Count} mods."
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "redraw":
|
|
||||||
{
|
|
||||||
if( args.Length > 1 )
|
|
||||||
{
|
|
||||||
ObjectReloader.RedrawObject( args[ 1 ], RedrawType.Redraw );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ObjectReloader.RedrawAll( RedrawType.Redraw );
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "debug":
|
|
||||||
{
|
|
||||||
Config.DebugMode = true;
|
|
||||||
Config.Save();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "enable":
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print( Enable()
|
|
||||||
? modsEnabled
|
|
||||||
: "Your mods are already enabled. To disable your mods, please run the following command instead: /penumbra disable" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "disable":
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print( Disable()
|
|
||||||
? modsDisabled
|
|
||||||
: "Your mods are already disabled. To enable your mods, please run the following command instead: /penumbra enable" );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "toggle":
|
|
||||||
{
|
|
||||||
SetEnabled( !Config.EnableMods );
|
|
||||||
Dalamud.Chat.Print( Config.EnableMods
|
|
||||||
? modsEnabled
|
|
||||||
: modsDisabled );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "unfix":
|
|
||||||
{
|
|
||||||
Config.FixMainWindow = false;
|
|
||||||
_configWindow.Flags &= ~( ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "collection":
|
|
||||||
{
|
|
||||||
if( args.Length == 2 )
|
|
||||||
{
|
|
||||||
args = args[ 1 ].Split( new[] { ' ' }, 2 );
|
|
||||||
if( args.Length == 2 )
|
|
||||||
{
|
|
||||||
SetCollection( args[ 0 ], args[ 1 ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Dalamud.Chat.Print( "Missing arguments, the correct command format is:"
|
|
||||||
+ " /penumbra collection {default} <collectionName> [|characterName]" );
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_configWindow.Toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect all relevant files for penumbra configuration.
|
// Collect all relevant files for penumbra configuration.
|
||||||
private static IReadOnlyList< FileInfo > PenumbraBackupFiles()
|
private static IReadOnlyList< FileInfo > PenumbraBackupFiles()
|
||||||
{
|
{
|
||||||
|
|
@ -568,7 +392,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
var path = Path.Combine( Dalamud.PluginInterface.DalamudAssetDirectory.Parent?.FullName ?? "INVALIDPATH", "devPlugins", "Penumbra" );
|
var path = Path.Combine( Dalamud.PluginInterface.DalamudAssetDirectory.Parent?.FullName ?? "INVALIDPATH", "devPlugins", "Penumbra" );
|
||||||
var dir = new DirectoryInfo( path );
|
var dir = new DirectoryInfo( path );
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -589,7 +413,7 @@ public class Penumbra : IDalamudPlugin
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
var checkedDirectory = Dalamud.PluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Name;
|
var checkedDirectory = Dalamud.PluginInterface.AssemblyLocation.Directory?.Parent?.Parent?.Name;
|
||||||
var ret = checkedDirectory?.Equals( "installedPlugins", StringComparison.OrdinalIgnoreCase ) ?? false;
|
var ret = checkedDirectory?.Equals( "installedPlugins", StringComparison.OrdinalIgnoreCase ) ?? false;
|
||||||
if( !ret )
|
if( !ret )
|
||||||
{
|
{
|
||||||
Log.Error( $"Penumbra is not correctly installed. Application loaded from \"{Dalamud.PluginInterface.AssemblyLocation.Directory!.FullName}\"." );
|
Log.Error( $"Penumbra is not correctly installed. Application loaded from \"{Dalamud.PluginInterface.AssemblyLocation.Directory!.FullName}\"." );
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue