mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add Ornaments, further work.
This commit is contained in:
parent
bda3c1f1ac
commit
f8c0702432
9 changed files with 273 additions and 161 deletions
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Penumbra.String;
|
||||
|
||||
|
|
@ -58,12 +60,12 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
?? Type switch
|
||||
{
|
||||
IdentifierType.Player => $"{PlayerName} ({HomeWorld})",
|
||||
IdentifierType.Owned => $"{PlayerName}s {Kind} {DataId} ({HomeWorld})",
|
||||
IdentifierType.Owned => $"{PlayerName}s {Kind.ToName()} {DataId} ({HomeWorld})",
|
||||
IdentifierType.Special => Special.ToName(),
|
||||
IdentifierType.Npc =>
|
||||
Index == ushort.MaxValue
|
||||
? $"{Kind} #{DataId}"
|
||||
: $"{Kind} #{DataId} at {Index}",
|
||||
? $"{Kind.ToName()} #{DataId}"
|
||||
: $"{Kind.ToName()} #{DataId} at {Index}",
|
||||
_ => "Invalid",
|
||||
};
|
||||
|
||||
|
|
@ -87,7 +89,6 @@ public readonly struct ActorIdentifier : IEquatable<ActorIdentifier>
|
|||
PlayerName = playerName;
|
||||
}
|
||||
|
||||
|
||||
public JObject ToJson()
|
||||
{
|
||||
var ret = new JObject { { nameof(Type), Type.ToString() } };
|
||||
|
|
@ -130,22 +131,19 @@ public static class ActorManagerExtensions
|
|||
if (manager == null)
|
||||
return lhs.Kind == rhs.Kind && lhs.DataId == rhs.DataId || lhs.DataId == uint.MaxValue || rhs.DataId == uint.MaxValue;
|
||||
|
||||
return lhs.Kind switch
|
||||
var dict = lhs.Kind switch
|
||||
{
|
||||
ObjectKind.MountType => manager.Mounts.TryGetValue(lhs.DataId, out var lhsName)
|
||||
&& manager.Mounts.TryGetValue(rhs.DataId, out var rhsName)
|
||||
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
|
||||
ObjectKind.Companion => manager.Companions.TryGetValue(lhs.DataId, out var lhsName)
|
||||
&& manager.Companions.TryGetValue(rhs.DataId, out var rhsName)
|
||||
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
|
||||
ObjectKind.BattleNpc => manager.BNpcs.TryGetValue(lhs.DataId, out var lhsName)
|
||||
&& manager.BNpcs.TryGetValue(rhs.DataId, out var rhsName)
|
||||
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
|
||||
ObjectKind.EventNpc => manager.ENpcs.TryGetValue(lhs.DataId, out var lhsName)
|
||||
&& manager.ENpcs.TryGetValue(rhs.DataId, out var rhsName)
|
||||
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase),
|
||||
_ => false,
|
||||
ObjectKind.MountType => manager.Mounts,
|
||||
ObjectKind.Companion => manager.Companions,
|
||||
(ObjectKind)15 => manager.Ornaments, // TODO: CS Update
|
||||
ObjectKind.BattleNpc => manager.BNpcs,
|
||||
ObjectKind.EventNpc => manager.ENpcs,
|
||||
_ => new Dictionary<uint, string>(),
|
||||
};
|
||||
|
||||
return dict.TryGetValue(lhs.DataId, out var lhsName)
|
||||
&& dict.TryGetValue(rhs.DataId, out var rhsName)
|
||||
&& lhsName.Equals(rhsName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static string ToName(this ObjectKind kind)
|
||||
|
|
@ -156,6 +154,7 @@ public static class ActorManagerExtensions
|
|||
ObjectKind.EventNpc => "Event NPC",
|
||||
ObjectKind.MountType => "Mount",
|
||||
ObjectKind.Companion => "Companion",
|
||||
(ObjectKind)15 => "Accessory", // TODO: CS update
|
||||
_ => kind.ToString(),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Dalamud;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Objects;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Utility;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Lumina.Text;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.String;
|
||||
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
||||
|
|
@ -31,6 +32,9 @@ public sealed partial class ActorManager : DataSharer
|
|||
/// <summary> Valid Companion names in title case by companion id. </summary>
|
||||
public IReadOnlyDictionary<uint, string> Companions { get; }
|
||||
|
||||
/// <summary> Valid ornament names by id. </summary>
|
||||
public IReadOnlyDictionary<uint, string> Ornaments { get; }
|
||||
|
||||
/// <summary> Valid BNPC names in title case by BNPC Name id. </summary>
|
||||
public IReadOnlyDictionary<uint, string> BNpcs { get; }
|
||||
|
||||
|
|
@ -54,6 +58,7 @@ public sealed partial class ActorManager : DataSharer
|
|||
Worlds = TryCatchData("Worlds", () => CreateWorldData(gameData));
|
||||
Mounts = TryCatchData("Mounts", () => CreateMountData(gameData));
|
||||
Companions = TryCatchData("Companions", () => CreateCompanionData(gameData));
|
||||
Ornaments = TryCatchData("Ornaments", () => CreateOrnamentData(gameData));
|
||||
BNpcs = TryCatchData("BNpcs", () => CreateBNpcData(gameData));
|
||||
ENpcs = TryCatchData("ENpcs", () => CreateENpcData(gameData));
|
||||
|
||||
|
|
@ -98,6 +103,7 @@ public sealed partial class ActorManager : DataSharer
|
|||
DisposeTag("Worlds");
|
||||
DisposeTag("Mounts");
|
||||
DisposeTag("Companions");
|
||||
DisposeTag("Ornaments");
|
||||
DisposeTag("BNpcs");
|
||||
DisposeTag("ENpcs");
|
||||
}
|
||||
|
|
@ -119,22 +125,50 @@ public sealed partial class ActorManager : DataSharer
|
|||
private IReadOnlyDictionary<uint, string> CreateMountData(DataManager gameData)
|
||||
=> gameData.GetExcelSheet<Mount>(Language)!
|
||||
.Where(m => m.Singular.RawData.Length > 0 && m.Order >= 0)
|
||||
.ToDictionary(m => m.RowId, m => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(m.Singular.ToDalamudString().ToString()));
|
||||
.ToDictionary(m => m.RowId, m => ToTitleCaseExtended(m.Singular, m.Article));
|
||||
|
||||
private IReadOnlyDictionary<uint, string> CreateCompanionData(DataManager gameData)
|
||||
=> gameData.GetExcelSheet<Companion>(Language)!
|
||||
.Where(c => c.Singular.RawData.Length > 0 && c.Order < ushort.MaxValue)
|
||||
.ToDictionary(c => c.RowId, c => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(c.Singular.ToDalamudString().ToString()));
|
||||
.ToDictionary(c => c.RowId, c => ToTitleCaseExtended(c.Singular, c.Article));
|
||||
|
||||
private IReadOnlyDictionary<uint, string> CreateOrnamentData(DataManager gameData)
|
||||
=> gameData.GetExcelSheet<Ornament>(Language)!
|
||||
.Where(o => o.Singular.RawData.Length > 0)
|
||||
.ToDictionary(o => o.RowId, o => ToTitleCaseExtended(o.Singular, o.Article));
|
||||
|
||||
private IReadOnlyDictionary<uint, string> CreateBNpcData(DataManager gameData)
|
||||
=> gameData.GetExcelSheet<BNpcName>(Language)!
|
||||
.Where(n => n.Singular.RawData.Length > 0)
|
||||
.ToDictionary(n => n.RowId, n => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(n.Singular.ToDalamudString().ToString()));
|
||||
.ToDictionary(n => n.RowId, n => ToTitleCaseExtended(n.Singular, n.Article));
|
||||
|
||||
private IReadOnlyDictionary<uint, string> CreateENpcData(DataManager gameData)
|
||||
=> gameData.GetExcelSheet<ENpcResident>(Language)!
|
||||
.Where(e => e.Singular.RawData.Length > 0)
|
||||
.ToDictionary(e => e.RowId, e => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(e.Singular.ToDalamudString().ToString()));
|
||||
.ToDictionary(e => e.RowId, e => ToTitleCaseExtended(e.Singular, e.Article));
|
||||
|
||||
private static string ToTitleCaseExtended(SeString s, sbyte article)
|
||||
{
|
||||
if (article == 1)
|
||||
return s.ToDalamudString().ToString();
|
||||
|
||||
var sb = new StringBuilder(s.ToDalamudString().ToString());
|
||||
var lastSpace = true;
|
||||
for (var i = 0; i < sb.Length; ++i)
|
||||
{
|
||||
if (sb[i] == ' ')
|
||||
{
|
||||
lastSpace = true;
|
||||
}
|
||||
else if (lastSpace)
|
||||
{
|
||||
lastSpace = false;
|
||||
sb[i] = char.ToUpperInvariant(sb[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
[Signature("0F B7 0D ?? ?? ?? ?? C7 85", ScanType = ScanType.StaticAddress)]
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public partial class ActorManager
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the world name including the All Worlds option.
|
||||
/// Return the world name including the Any World option.
|
||||
/// </summary>
|
||||
public string ToWorldName(ushort worldId)
|
||||
=> worldId == ushort.MaxValue ? "Any World" : Worlds.TryGetValue(worldId, out var name) ? name : "Invalid";
|
||||
|
|
@ -98,6 +98,7 @@ public partial class ActorManager
|
|||
{
|
||||
ObjectKind.MountType => Mounts.TryGetValue(dataId, out name),
|
||||
ObjectKind.Companion => Companions.TryGetValue(dataId, out name),
|
||||
(ObjectKind)15 => Ornaments.TryGetValue(dataId, out name), // TODO: CS Update
|
||||
ObjectKind.BattleNpc => BNpcs.TryGetValue(dataId, out name),
|
||||
ObjectKind.EventNpc => ENpcs.TryGetValue(dataId, out name),
|
||||
_ => false,
|
||||
|
|
@ -154,6 +155,7 @@ public partial class ActorManager
|
|||
case ObjectKind.EventNpc: return CreateNpc(ObjectKind.EventNpc, actor->DataID, actor->ObjectIndex);
|
||||
case ObjectKind.MountType:
|
||||
case ObjectKind.Companion:
|
||||
case (ObjectKind)15: // TODO: CS Update
|
||||
{
|
||||
if (actor->ObjectIndex % 2 == 0)
|
||||
return ActorIdentifier.Invalid;
|
||||
|
|
@ -173,11 +175,12 @@ public partial class ActorManager
|
|||
/// Obtain the current companion ID for an object by its actor and owner.
|
||||
/// </summary>
|
||||
private unsafe uint GetCompanionId(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* actor,
|
||||
FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner)
|
||||
FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* owner) // TODO: CS Update
|
||||
{
|
||||
return (ObjectKind)actor->ObjectKind switch
|
||||
{
|
||||
ObjectKind.MountType => *(ushort*)((byte*)owner + 0x668),
|
||||
ObjectKind.MountType => *(ushort*)((byte*)owner + 0x650 + 0x18),
|
||||
(ObjectKind)15 => *(ushort*)((byte*)owner + 0x860 + 0x18),
|
||||
ObjectKind.Companion => *(ushort*)((byte*)actor + 0x1AAC),
|
||||
_ => actor->DataID,
|
||||
};
|
||||
|
|
@ -196,6 +199,12 @@ public partial class ActorManager
|
|||
_ => ActorIdentifier.Invalid,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Only use this if you are sure the input is valid.
|
||||
/// </summary>
|
||||
public ActorIdentifier CreateIndividualUnchecked(IdentifierType type, ByteString name, ushort homeWorld, ObjectKind kind, uint dataId)
|
||||
=> new(type, kind, homeWorld, dataId, name);
|
||||
|
||||
public ActorIdentifier CreatePlayer(ByteString name, ushort homeWorld)
|
||||
{
|
||||
if (!VerifyWorld(homeWorld) || !VerifyPlayerName(name.Span))
|
||||
|
|
@ -345,6 +354,7 @@ public partial class ActorManager
|
|||
{
|
||||
ObjectKind.MountType => Mounts.ContainsKey(dataId),
|
||||
ObjectKind.Companion => Companions.ContainsKey(dataId),
|
||||
(ObjectKind)15 => Ornaments.ContainsKey(dataId), // TODO: CS Update
|
||||
ObjectKind.BattleNpc => BNpcs.ContainsKey(dataId),
|
||||
_ => false,
|
||||
};
|
||||
|
|
@ -355,6 +365,7 @@ public partial class ActorManager
|
|||
{
|
||||
ObjectKind.MountType => Mounts.ContainsKey(dataId),
|
||||
ObjectKind.Companion => Companions.ContainsKey(dataId),
|
||||
(ObjectKind)15 => Ornaments.ContainsKey(dataId), // TODO: CS Update
|
||||
ObjectKind.BattleNpc => BNpcs.ContainsKey(dataId),
|
||||
ObjectKind.EventNpc => ENpcs.ContainsKey(dataId),
|
||||
_ => false,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Penumbra.Collections;
|
||||
|
||||
|
|
@ -43,8 +44,8 @@ public partial class ModCollection
|
|||
=> _characters;
|
||||
|
||||
// If a name does not correspond to a character, return the default collection instead.
|
||||
public ModCollection Character( string name )
|
||||
=> _characters.TryGetValue( name, out var c ) ? c : Default;
|
||||
public ModCollection Individual( ActorIdentifier identifier )
|
||||
=> Individuals.Individuals.TryGetValue( identifier, out var c ) ? c : Default;
|
||||
|
||||
// Special Collections
|
||||
private readonly ModCollection?[] _specialCollections = new ModCollection?[Enum.GetValues< CollectionType >().Length - 4];
|
||||
|
|
@ -62,7 +63,7 @@ public partial class ModCollection
|
|||
CollectionType.Default => Default,
|
||||
CollectionType.Interface => Interface,
|
||||
CollectionType.Current => Current,
|
||||
CollectionType.Character => name != null ? _characters.TryGetValue( name, out var c ) ? c : null : null,
|
||||
CollectionType.Individual => name != null ? _characters.TryGetValue( name, out var c ) ? c : null : null,
|
||||
CollectionType.Inactive => name != null ? ByName( name, out var c ) ? c : null : null,
|
||||
_ => null,
|
||||
};
|
||||
|
|
@ -76,7 +77,7 @@ public partial class ModCollection
|
|||
CollectionType.Default => Default.Index,
|
||||
CollectionType.Interface => Interface.Index,
|
||||
CollectionType.Current => Current.Index,
|
||||
CollectionType.Character => characterName?.Length > 0
|
||||
CollectionType.Individual => characterName?.Length > 0
|
||||
? _characters.TryGetValue( characterName, out var c )
|
||||
? c.Index
|
||||
: Default.Index
|
||||
|
|
@ -113,7 +114,7 @@ public partial class ModCollection
|
|||
case CollectionType.Current:
|
||||
Current = newCollection;
|
||||
break;
|
||||
case CollectionType.Character:
|
||||
case CollectionType.Individual:
|
||||
_characters[ characterName! ] = newCollection;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -176,7 +177,7 @@ public partial class ModCollection
|
|||
}
|
||||
|
||||
_characters[ characterName ] = Default;
|
||||
CollectionChanged.Invoke( CollectionType.Character, null, Default, characterName );
|
||||
CollectionChanged.Invoke( CollectionType.Individual, null, Default, characterName );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +188,7 @@ public partial class ModCollection
|
|||
{
|
||||
RemoveCache( collection.Index );
|
||||
_characters.Remove( characterName );
|
||||
CollectionChanged.Invoke( CollectionType.Character, collection, null, characterName );
|
||||
CollectionChanged.Invoke( CollectionType.Individual, collection, null, characterName );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,8 +97,7 @@ public enum CollectionType : byte
|
|||
Inactive, // A collection was added or removed
|
||||
Default, // The default collection was changed
|
||||
Interface, // The ui collection was changed
|
||||
Character, // A character collection was changed
|
||||
Individual, // An Individual collection was changed
|
||||
Individual, // An individual collection was changed
|
||||
Current, // The current collection was changed
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +287,7 @@ public static class CollectionTypeExtensions
|
|||
CollectionType.Inactive => "Collection",
|
||||
CollectionType.Default => "Default",
|
||||
CollectionType.Interface => "Interface",
|
||||
CollectionType.Character => "Character",
|
||||
CollectionType.Individual => "Character",
|
||||
CollectionType.Current => "Current",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ public sealed partial class IndividualCollections : IReadOnlyList< (string Displ
|
|||
return true;
|
||||
}
|
||||
|
||||
identifier = _manager.CreateIndividual( identifier.Type, identifier.PlayerName, ushort.MaxValue, identifier.Kind, identifier.DataId );
|
||||
identifier = _manager.CreateIndividualUnchecked( identifier.Type, identifier.PlayerName, ushort.MaxValue, identifier.Kind, identifier.DataId );
|
||||
if( identifier.IsValid && _individuals.TryGetValue( identifier, out collection ) )
|
||||
{
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ public sealed partial class IndividualCollections
|
|||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
return table.Where( kvp => kvp.Value == name )
|
||||
.Select( kvp => manager.CreateIndividual( identifier.Type, identifier.PlayerName, identifier.HomeWorld, identifier.Kind, kvp.Key ) ).ToArray();
|
||||
.Select( kvp => manager.CreateIndividualUnchecked( identifier.Type, identifier.PlayerName, identifier.HomeWorld, identifier.Kind, kvp.Key ) ).ToArray();
|
||||
}
|
||||
|
||||
return identifier.Type switch
|
||||
|
|
@ -115,9 +115,27 @@ public sealed partial class IndividualCollections
|
|||
};
|
||||
}
|
||||
|
||||
public bool Add( string displayName, ActorIdentifier[] identifiers, ModCollection collection )
|
||||
public bool Add( ActorIdentifier[] identifiers, ModCollection collection )
|
||||
{
|
||||
if( CanAdd( identifiers ) != AddResult.Valid || _assignments.ContainsKey( displayName ) )
|
||||
if( identifiers.Length == 0 || !identifiers[ 0 ].IsValid )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var name = identifiers[ 0 ].Type switch
|
||||
{
|
||||
IdentifierType.Player => $"{identifiers[ 0 ].PlayerName} ({_manager.ToWorldName( identifiers[ 0 ].HomeWorld )})",
|
||||
IdentifierType.Owned =>
|
||||
$"{identifiers[ 0 ].PlayerName} ({_manager.ToWorldName( identifiers[ 0 ].HomeWorld )})'s {_manager.ToName( identifiers[ 0 ].Kind, identifiers[ 0 ].DataId )}",
|
||||
IdentifierType.Npc => $"{_manager.ToName( identifiers[ 0 ].Kind, identifiers[ 0 ].DataId )} ({identifiers[ 0 ].Kind})",
|
||||
_ => string.Empty,
|
||||
};
|
||||
return Add( name, identifiers, collection );
|
||||
}
|
||||
|
||||
private bool Add( string displayName, ActorIdentifier[] identifiers, ModCollection collection )
|
||||
{
|
||||
if( CanAdd( identifiers ) != AddResult.Valid || displayName.Length == 0 || _assignments.ContainsKey( displayName ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
|
|
@ -7,8 +8,10 @@ using Penumbra.Collections;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Interface.Components;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Lumina.Data.Parsing;
|
||||
|
||||
namespace Penumbra.UI;
|
||||
|
||||
|
|
@ -18,7 +21,7 @@ public partial class ConfigWindow
|
|||
{
|
||||
private sealed class WorldCombo : FilterComboCache< KeyValuePair< ushort, string > >
|
||||
{
|
||||
private static readonly KeyValuePair< ushort, string > AllWorldPair = new(ushort.MaxValue, "All Worlds");
|
||||
private static readonly KeyValuePair< ushort, string > AllWorldPair = new(ushort.MaxValue, "Any World");
|
||||
|
||||
public WorldCombo( IReadOnlyDictionary< ushort, string > worlds )
|
||||
: base( worlds.OrderBy( kvp => kvp.Value ).Prepend( AllWorldPair ) )
|
||||
|
|
@ -30,7 +33,7 @@ public partial class ConfigWindow
|
|||
protected override string ToString( KeyValuePair< ushort, string > obj )
|
||||
=> obj.Value;
|
||||
|
||||
public void Draw( float width )
|
||||
public bool Draw( float width )
|
||||
=> Draw( "##worldCombo", CurrentSelection.Value, width, ImGui.GetTextLineHeightWithSpacing() );
|
||||
}
|
||||
|
||||
|
|
@ -57,147 +60,78 @@ public partial class ConfigWindow
|
|||
return ret;
|
||||
}
|
||||
|
||||
public void Draw( float width )
|
||||
public bool Draw( float width )
|
||||
=> Draw( _label, CurrentSelection.Name, width, ImGui.GetTextLineHeightWithSpacing() );
|
||||
}
|
||||
|
||||
|
||||
// Input Selections.
|
||||
private string _newCharacterName = string.Empty;
|
||||
private IdentifierType _newType = IdentifierType.Player;
|
||||
private ObjectKind _newKind = ObjectKind.BattleNpc;
|
||||
private string _newCharacterName = string.Empty;
|
||||
private ObjectKind _newKind = ObjectKind.BattleNpc;
|
||||
|
||||
private readonly WorldCombo _worldCombo = new(Penumbra.Actors.Worlds);
|
||||
private readonly NpcCombo _mountCombo = new("##mountCombo", Penumbra.Actors.Mounts);
|
||||
private readonly NpcCombo _companionCombo = new("##companionCombo", Penumbra.Actors.Companions);
|
||||
private readonly NpcCombo _ornamentCombo = new("##ornamentCombo", Penumbra.Actors.Ornaments);
|
||||
private readonly NpcCombo _bnpcCombo = new("##bnpcCombo", Penumbra.Actors.BNpcs);
|
||||
private readonly NpcCombo _enpcCombo = new("##enpcCombo", Penumbra.Actors.ENpcs);
|
||||
|
||||
private void DrawNewIdentifierOptions( float width )
|
||||
private const string NewPlayerTooltipEmpty = "Please enter a valid player name and choose an available world or 'Any World'.";
|
||||
private const string NewPlayerTooltipInvalid = "The entered name is not a valid name for a player character.";
|
||||
private const string AlreadyAssigned = "The Individual you specified has already been assigned a collection.";
|
||||
private const string NewNpcTooltipEmpty = "Please select a valid NPC from the drop down menu first.";
|
||||
|
||||
private ActorIdentifier[] _newPlayerIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
private string _newPlayerTooltip = NewPlayerTooltipEmpty;
|
||||
private ActorIdentifier[] _newNpcIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
private string _newNpcTooltip = NewNpcTooltipEmpty;
|
||||
private ActorIdentifier[] _newOwnedIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
private string _newOwnedTooltip = NewPlayerTooltipEmpty;
|
||||
|
||||
private bool DrawNewObjectKindOptions( float width )
|
||||
{
|
||||
ImGui.SetNextItemWidth( width );
|
||||
using var combo = ImRaii.Combo( "##newType", _newType.ToString() );
|
||||
if( combo )
|
||||
using var combo = ImRaii.Combo( "##newKind", _newKind.ToName() );
|
||||
if( !combo )
|
||||
{
|
||||
if( ImGui.Selectable( IdentifierType.Player.ToString(), _newType == IdentifierType.Player ) )
|
||||
{
|
||||
_newType = IdentifierType.Player;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if( ImGui.Selectable( IdentifierType.Owned.ToString(), _newType == IdentifierType.Owned ) )
|
||||
var ret = false;
|
||||
foreach( var kind in new[] { ObjectKind.BattleNpc, ObjectKind.EventNpc, ObjectKind.Companion, ObjectKind.MountType, ( ObjectKind )15 } ) // TODO: CS Update
|
||||
{
|
||||
if( ImGui.Selectable( kind.ToName(), _newKind == kind ) )
|
||||
{
|
||||
_newType = IdentifierType.Owned;
|
||||
}
|
||||
|
||||
if( ImGui.Selectable( IdentifierType.Npc.ToString(), _newType == IdentifierType.Npc ) )
|
||||
{
|
||||
_newType = IdentifierType.Npc;
|
||||
_newKind = kind;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void DrawNewObjectKindOptions( float width )
|
||||
{
|
||||
ImGui.SetNextItemWidth( width );
|
||||
using var combo = ImRaii.Combo( "##newKind", _newKind.ToString() );
|
||||
if( combo )
|
||||
{
|
||||
if( ImGui.Selectable( ObjectKind.BattleNpc.ToString(), _newKind == ObjectKind.BattleNpc ) )
|
||||
{
|
||||
_newKind = ObjectKind.BattleNpc;
|
||||
}
|
||||
|
||||
if( ImGui.Selectable( ObjectKind.EventNpc.ToString(), _newKind == ObjectKind.EventNpc ) )
|
||||
{
|
||||
_newKind = ObjectKind.EventNpc;
|
||||
}
|
||||
|
||||
if( ImGui.Selectable( ObjectKind.Companion.ToString(), _newKind == ObjectKind.Companion ) )
|
||||
{
|
||||
_newKind = ObjectKind.Companion;
|
||||
}
|
||||
|
||||
if( ImGui.Selectable( ObjectKind.MountType.ToString(), _newKind == ObjectKind.MountType ) )
|
||||
{
|
||||
_newKind = ObjectKind.MountType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We do not check for valid character names.
|
||||
private void DrawNewCharacterCollection()
|
||||
{
|
||||
const string description = "Character Collections apply specifically to individual game objects of the given name.\n"
|
||||
+ $"More general {GroupAssignment} or the {DefaultCollection} do not apply if an .\n"
|
||||
+ "Certain actors - like the ones in cutscenes or preview windows - will try to use appropriate character collections.\n";
|
||||
|
||||
var width = ( _window._inputTextWidth.X - 2 * ImGui.GetStyle().ItemSpacing.X ) / 3;
|
||||
DrawNewIdentifierOptions( width );
|
||||
ImGui.SameLine();
|
||||
using( var dis = ImRaii.Disabled( _newType == IdentifierType.Npc ) )
|
||||
{
|
||||
_worldCombo.Draw( width );
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
using( var dis = ImRaii.Disabled( _newType == IdentifierType.Player ) )
|
||||
{
|
||||
DrawNewObjectKindOptions( width );
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth( _window._inputTextWidth.X );
|
||||
using( var dis = ImRaii.Disabled( _newType == IdentifierType.Npc ) )
|
||||
{
|
||||
ImGui.InputTextWithHint( "##NewCharacter", "Character Name...", ref _newCharacterName, 32 );
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
var disabled = _newCharacterName.Length == 0;
|
||||
var tt = disabled
|
||||
? $"Please enter the name of a {ConditionalIndividual} before assigning the collection.\n\n" + description
|
||||
: description;
|
||||
if( ImGuiUtil.DrawDisabledButton( $"Assign {ConditionalIndividual}", new Vector2( 120 * ImGuiHelpers.GlobalScale, 0 ), tt,
|
||||
disabled ) )
|
||||
{
|
||||
Penumbra.CollectionManager.CreateCharacterCollection( _newCharacterName );
|
||||
_newCharacterName = string.Empty;
|
||||
}
|
||||
|
||||
using( var dis = ImRaii.Disabled( _newType == IdentifierType.Player ) )
|
||||
{
|
||||
switch( _newKind )
|
||||
{
|
||||
case ObjectKind.BattleNpc:
|
||||
_bnpcCombo.Draw( _window._inputTextWidth.X );
|
||||
break;
|
||||
case ObjectKind.EventNpc:
|
||||
_enpcCombo.Draw( _window._inputTextWidth.X );
|
||||
break;
|
||||
case ObjectKind.Companion:
|
||||
_companionCombo.Draw( _window._inputTextWidth.X );
|
||||
break;
|
||||
case ObjectKind.MountType:
|
||||
_mountCombo.Draw( _window._inputTextWidth.X );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawIndividualAssignments()
|
||||
{
|
||||
using var _ = ImRaii.Group();
|
||||
using var _ = ImRaii.Group();
|
||||
using var mainId = ImRaii.PushId( "Individual" );
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( $"Individual {ConditionalIndividual}s" );
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker( "Individual Collections apply specifically to individual game objects that fulfill the given criteria.\n"
|
||||
+ $"More general {GroupAssignment} or the {DefaultCollection} do not apply if an Individual Collection takes effect.\n"
|
||||
+ "Certain related actors - like the ones in cutscenes or preview windows - will try to use appropriate individual collections." );
|
||||
ImGui.Separator();
|
||||
foreach( var name in Penumbra.CollectionManager.Characters.Keys.OrderBy( k => k ).ToArray() )
|
||||
for( var i = 0; i < Penumbra.CollectionManager.Individuals.Count; ++i )
|
||||
{
|
||||
using var id = ImRaii.PushId( name );
|
||||
var (name, collection) = Penumbra.CollectionManager.Individuals[ i ];
|
||||
using var id = ImRaii.PushId( i );
|
||||
DrawCollectionSelector( string.Empty, _window._inputTextWidth.X, CollectionType.Character, true, name );
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Trash.ToIconString(), _window._iconButtonSize, string.Empty,
|
||||
false, true ) )
|
||||
{
|
||||
Penumbra.CollectionManager.RemoveCharacterCollection( name );
|
||||
Penumbra.CollectionManager.Individuals.Delete( i );
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
|
|
@ -206,7 +140,120 @@ public partial class ConfigWindow
|
|||
}
|
||||
|
||||
ImGui.Dummy( Vector2.Zero );
|
||||
DrawNewCharacterCollection();
|
||||
DrawNewIndividualCollection();
|
||||
}
|
||||
|
||||
private bool DrawNewPlayerCollection( Vector2 buttonWidth, float width )
|
||||
{
|
||||
var change = _worldCombo.Draw( width );
|
||||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth( _window._inputTextWidth.X - ImGui.GetStyle().ItemSpacing.X - width );
|
||||
change |= ImGui.InputTextWithHint( "##NewCharacter", "Character Name...", ref _newCharacterName, 32 );
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( "Assign Player", buttonWidth, _newPlayerTooltip, _newPlayerTooltip.Length > 0 || _newPlayerIdentifiers.Length == 0 ) )
|
||||
{
|
||||
Penumbra.CollectionManager.Individuals.Add( _newPlayerIdentifiers, Penumbra.CollectionManager.Default );
|
||||
change = true;
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
private bool DrawNewNpcCollection( NpcCombo combo, Vector2 buttonWidth, float width )
|
||||
{
|
||||
var comboWidth = _window._inputTextWidth.X - ImGui.GetStyle().ItemSpacing.X - width;
|
||||
var change = DrawNewObjectKindOptions( width );
|
||||
ImGui.SameLine();
|
||||
change |= combo.Draw( comboWidth );
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( "Assign NPC", buttonWidth, _newNpcTooltip, _newNpcIdentifiers.Length == 0 || _newNpcTooltip.Length > 0 ) )
|
||||
{
|
||||
Penumbra.CollectionManager.Individuals.Add( _newNpcIdentifiers, Penumbra.CollectionManager.Default );
|
||||
change = true;
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
private bool DrawNewOwnedCollection( Vector2 buttonWidth )
|
||||
{
|
||||
var oldPos = ImGui.GetCursorPos();
|
||||
ImGui.SameLine();
|
||||
ImGui.SetCursorPos( ImGui.GetCursorPos() + new Vector2( -ImGui.GetStyle().ItemSpacing.X / 2, ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y ) / 2 );
|
||||
if( ImGuiUtil.DrawDisabledButton( "Assign Owned NPC", buttonWidth, _newOwnedTooltip, _newOwnedIdentifiers.Length == 0 || _newOwnedTooltip.Length > 0 ) )
|
||||
{
|
||||
Penumbra.CollectionManager.Individuals.Add( _newOwnedIdentifiers, Penumbra.CollectionManager.Default );
|
||||
return true;
|
||||
}
|
||||
|
||||
ImGui.SetCursorPos( oldPos );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private NpcCombo GetNpcCombo( ObjectKind kind )
|
||||
=> kind switch
|
||||
{
|
||||
ObjectKind.BattleNpc => _bnpcCombo,
|
||||
ObjectKind.EventNpc => _enpcCombo,
|
||||
ObjectKind.MountType => _mountCombo,
|
||||
ObjectKind.Companion => _companionCombo,
|
||||
( ObjectKind )15 => _ornamentCombo, // TODO: CS update
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
private void DrawNewIndividualCollection()
|
||||
{
|
||||
var width = ( _window._inputTextWidth.X - 2 * ImGui.GetStyle().ItemSpacing.X ) / 3;
|
||||
var buttonWidth = new Vector2( 90 * ImGuiHelpers.GlobalScale, 0 );
|
||||
|
||||
var combo = GetNpcCombo( _newKind );
|
||||
var change = DrawNewPlayerCollection( buttonWidth, width );
|
||||
change |= DrawNewOwnedCollection( Vector2.Zero );
|
||||
change |= DrawNewNpcCollection( combo, buttonWidth, width );
|
||||
|
||||
if( change )
|
||||
{
|
||||
UpdateIdentifiers();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateIdentifiers()
|
||||
{
|
||||
var combo = GetNpcCombo( _newKind );
|
||||
_newPlayerTooltip = Penumbra.CollectionManager.Individuals.CanAdd( IdentifierType.Player, _newCharacterName, _worldCombo.CurrentSelection.Key, ObjectKind.None,
|
||||
Array.Empty< uint >(), out _newPlayerIdentifiers ) switch
|
||||
{
|
||||
_ when _newCharacterName.Length == 0 => NewPlayerTooltipEmpty,
|
||||
IndividualCollections.AddResult.Invalid => NewPlayerTooltipInvalid,
|
||||
IndividualCollections.AddResult.AlreadySet => AlreadyAssigned,
|
||||
_ => string.Empty,
|
||||
};
|
||||
if( combo.CurrentSelection.Ids != null )
|
||||
{
|
||||
_newNpcTooltip = Penumbra.CollectionManager.Individuals.CanAdd( IdentifierType.Npc, string.Empty, ushort.MaxValue, _newKind,
|
||||
combo.CurrentSelection.Ids, out _newNpcIdentifiers ) switch
|
||||
{
|
||||
IndividualCollections.AddResult.AlreadySet => AlreadyAssigned,
|
||||
_ => string.Empty,
|
||||
};
|
||||
_newOwnedTooltip = Penumbra.CollectionManager.Individuals.CanAdd( IdentifierType.Owned, _newCharacterName, _worldCombo.CurrentSelection.Key, _newKind,
|
||||
combo.CurrentSelection.Ids, out _newOwnedIdentifiers ) switch
|
||||
{
|
||||
_ when _newCharacterName.Length == 0 => NewPlayerTooltipEmpty,
|
||||
IndividualCollections.AddResult.Invalid => NewPlayerTooltipInvalid,
|
||||
IndividualCollections.AddResult.AlreadySet => AlreadyAssigned,
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_newNpcTooltip = NewNpcTooltipEmpty;
|
||||
_newOwnedTooltip = NewNpcTooltipEmpty;
|
||||
_newNpcIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
_newOwnedIdentifiers = Array.Empty< ActorIdentifier >();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ public partial class ConfigWindow
|
|||
public void Draw()
|
||||
{
|
||||
var preview = CurrentIdx >= 0 ? Items[ CurrentIdx ].Item2 : string.Empty;
|
||||
Draw(_label, preview, ref CurrentIdx, _unscaledWidth * ImGuiHelpers.GlobalScale, ImGui.GetTextLineHeightWithSpacing());
|
||||
Draw( _label, preview, ref CurrentIdx, _unscaledWidth * ImGuiHelpers.GlobalScale, ImGui.GetTextLineHeightWithSpacing() );
|
||||
}
|
||||
|
||||
protected override string ToString( (CollectionType, string, string) obj )
|
||||
|
|
@ -176,15 +176,16 @@ public partial class ConfigWindow
|
|||
|
||||
private readonly SpecialCombo _specialCollectionCombo = new("##NewSpecial", 350);
|
||||
|
||||
private const string CharacterGroupDescription = $"{CharacterGroups} apply to certain types of characters based on a condition.\n"
|
||||
+ $"All of them take precedence before the {DefaultCollection},\n"
|
||||
+ $"but all {IndividualAssignments} take precedence before them.";
|
||||
|
||||
|
||||
// We do not check for valid character names.
|
||||
private void DrawNewSpecialCollection()
|
||||
{
|
||||
const string description = $"{CharacterGroups} apply to certain types of characters based on a condition.\n"
|
||||
+ $"All of them take precedence before the {DefaultCollection},\n"
|
||||
+ $"but all {IndividualAssignments} take precedence before them.";
|
||||
|
||||
ImGui.SetNextItemWidth( _window._inputTextWidth.X );
|
||||
if( _specialCollectionCombo.CurrentIdx == -1
|
||||
if( _specialCollectionCombo.CurrentIdx == -1
|
||||
|| Penumbra.CollectionManager.ByType( _specialCollectionCombo.CurrentType!.Value.Item1 ) != null )
|
||||
{
|
||||
_specialCollectionCombo.ResetFilter();
|
||||
|
|
@ -201,8 +202,8 @@ public partial class ConfigWindow
|
|||
ImGui.SameLine();
|
||||
var disabled = _specialCollectionCombo.CurrentType == null;
|
||||
var tt = disabled
|
||||
? $"Please select a condition for a {GroupAssignment} before creating the collection.\n\n" + description
|
||||
: description;
|
||||
? $"Please select a condition for a {GroupAssignment} before creating the collection.\n\n" + CharacterGroupDescription
|
||||
: CharacterGroupDescription;
|
||||
if( ImGuiUtil.DrawDisabledButton( $"Assign {ConditionalGroup}", new Vector2( 120 * ImGuiHelpers.GlobalScale, 0 ), tt, disabled ) )
|
||||
{
|
||||
Penumbra.CollectionManager.CreateSpecialCollection( _specialCollectionCombo.CurrentType!.Value.Item1 );
|
||||
|
|
@ -237,7 +238,9 @@ public partial class ConfigWindow
|
|||
private void DrawSpecialAssignments()
|
||||
{
|
||||
using var _ = ImRaii.Group();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( CharacterGroups );
|
||||
ImGuiComponents.HelpMarker( CharacterGroupDescription );
|
||||
ImGui.Separator();
|
||||
DrawSpecialCollections();
|
||||
ImGui.Dummy( Vector2.Zero );
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue