mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Allow item swapping between from accessories and hats to other accessory types.
This commit is contained in:
parent
19dde3cbc4
commit
1b7360f8be
6 changed files with 243 additions and 38 deletions
|
|
@ -35,6 +35,16 @@ public readonly struct EqdpManipulation : IMetaManipulation< EqdpManipulation >
|
|||
Entry = Eqdp.Mask( Slot ) & entry;
|
||||
}
|
||||
|
||||
public EqdpManipulation Copy( EqdpManipulation entry )
|
||||
{
|
||||
if( entry.Slot != Slot )
|
||||
{
|
||||
var (bit1, bit2) = entry.Entry.ToBits( entry.Slot );
|
||||
return new EqdpManipulation(Eqdp.FromSlotAndBits( Slot, bit1, bit2 ), Slot, Gender, Race, SetId);
|
||||
}
|
||||
return new EqdpManipulation(entry.Entry, Slot, Gender, Race, SetId);
|
||||
}
|
||||
|
||||
public EqdpManipulation Copy( EqdpEntry entry )
|
||||
=> new(entry, Slot, Gender, Race, SetId);
|
||||
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ public readonly struct MetaManipulation : IEquatable< MetaManipulation >, ICompa
|
|||
{
|
||||
Type.Eqp => Eqp.Copy( other.Eqp.Entry ),
|
||||
Type.Gmp => Gmp.Copy( other.Gmp.Entry ),
|
||||
Type.Eqdp => Eqdp.Copy( other.Eqdp.Entry ),
|
||||
Type.Eqdp => Eqdp.Copy( other.Eqdp ),
|
||||
Type.Est => Est.Copy( other.Est.Entry ),
|
||||
Type.Rsp => Rsp.Copy( other.Rsp.Entry ),
|
||||
Type.Imc => Imc.Copy( other.Imc.Entry ),
|
||||
|
|
|
|||
|
|
@ -33,6 +33,69 @@ public static class EquipmentSwap
|
|||
: Array.Empty< EquipSlot >();
|
||||
}
|
||||
|
||||
public static Item[] CreateTypeSwap( List< Swap > swaps, Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips,
|
||||
EquipSlot slotFrom, Item itemFrom, EquipSlot slotTo, Item itemTo )
|
||||
{
|
||||
LookupItem( itemFrom, out var actualSlotFrom, out var idFrom, out var variantFrom );
|
||||
LookupItem( itemTo, out var actualSlotTo, out var idTo, out var variantTo );
|
||||
if( actualSlotFrom != slotFrom.ToSlot() || actualSlotTo != slotTo.ToSlot() )
|
||||
{
|
||||
throw new ItemSwap.InvalidItemTypeException();
|
||||
}
|
||||
|
||||
var ( imcFileFrom, variants, affectedItems ) = GetVariants( slotFrom, idFrom, idTo, variantFrom );
|
||||
var imcManip = new ImcManipulation( slotTo, variantTo, idTo.Value, default );
|
||||
var imcFileTo = new ImcFile( imcManip );
|
||||
var skipFemale = false;
|
||||
var skipMale = false;
|
||||
var mtrlVariantTo = manips( imcManip.Copy( imcFileTo.GetEntry( ImcFile.PartIndex( slotTo ), variantTo ) ) ).Imc.Entry.MaterialId;
|
||||
foreach( var gr in Enum.GetValues< GenderRace >() )
|
||||
{
|
||||
switch( gr.Split().Item1 )
|
||||
{
|
||||
case Gender.Male when skipMale: continue;
|
||||
case Gender.Female when skipFemale: continue;
|
||||
case Gender.MaleNpc when skipMale: continue;
|
||||
case Gender.FemaleNpc when skipFemale: continue;
|
||||
}
|
||||
|
||||
if( CharacterUtility.EqdpIdx( gr, true ) < 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var eqdp = CreateEqdp( redirections, manips, slotFrom, slotTo, gr, idFrom, idTo, mtrlVariantTo );
|
||||
if( eqdp != null )
|
||||
{
|
||||
swaps.Add( eqdp );
|
||||
}
|
||||
}
|
||||
catch( ItemSwap.MissingFileException e )
|
||||
{
|
||||
switch( gr )
|
||||
{
|
||||
case GenderRace.MidlanderMale when e.Type == ResourceType.Mdl:
|
||||
skipMale = true;
|
||||
continue;
|
||||
case GenderRace.MidlanderFemale when e.Type == ResourceType.Mdl:
|
||||
skipFemale = true;
|
||||
continue;
|
||||
default: throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach( var variant in variants )
|
||||
{
|
||||
var imc = CreateImc( redirections, manips, slotFrom, slotTo, idFrom, idTo, variant, variantTo, imcFileFrom, imcFileTo );
|
||||
swaps.Add( imc );
|
||||
}
|
||||
|
||||
return affectedItems;
|
||||
}
|
||||
|
||||
public static Item[] CreateItemSwap( List< Swap > swaps, Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, Item itemFrom,
|
||||
Item itemTo, bool rFinger = true, bool lFinger = true )
|
||||
{
|
||||
|
|
@ -59,9 +122,9 @@ public static class EquipmentSwap
|
|||
var affectedItems = Array.Empty< Item >();
|
||||
foreach( var slot in ConvertSlots( slotFrom, rFinger, lFinger ) )
|
||||
{
|
||||
(var imcFileFrom, var variants, affectedItems) = GetVariants( slot, idFrom, idTo, variantFrom );
|
||||
( var imcFileFrom, var variants, affectedItems ) = GetVariants( slot, idFrom, idTo, variantFrom );
|
||||
var imcManip = new ImcManipulation( slot, variantTo, idTo.Value, default );
|
||||
var imcFileTo = new ImcFile( imcManip);
|
||||
var imcFileTo = new ImcFile( imcManip );
|
||||
|
||||
var isAccessory = slot.IsAccessory();
|
||||
var estType = slot switch
|
||||
|
|
@ -89,7 +152,7 @@ public static class EquipmentSwap
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var eqdp = CreateEqdp( redirections, manips, slot, gr, idFrom, idTo, mtrlVariantTo );
|
||||
|
|
@ -99,7 +162,7 @@ public static class EquipmentSwap
|
|||
}
|
||||
|
||||
var ownMdl = eqdp?.SwapApplied.Eqdp.Entry.ToBits( slot ).Item2 ?? false;
|
||||
var est = ItemSwap.CreateEst( redirections, manips, estType, gr, idFrom, idTo, ownMdl );
|
||||
var est = ItemSwap.CreateEst( redirections, manips, estType, gr, idFrom, idTo, ownMdl );
|
||||
if( est != null )
|
||||
{
|
||||
swaps.Add( est );
|
||||
|
|
@ -132,15 +195,18 @@ public static class EquipmentSwap
|
|||
|
||||
public static MetaSwap? CreateEqdp( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slot, GenderRace gr, SetId idFrom,
|
||||
SetId idTo, byte mtrlTo )
|
||||
=> CreateEqdp( redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo );
|
||||
public static MetaSwap? CreateEqdp( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom,
|
||||
SetId idTo, byte mtrlTo )
|
||||
{
|
||||
var (gender, race) = gr.Split();
|
||||
var eqdpFrom = new EqdpManipulation( ExpandedEqdpFile.GetDefault( gr, slot.IsAccessory(), idFrom.Value ), slot, gender, race, idFrom.Value );
|
||||
var eqdpTo = new EqdpManipulation( ExpandedEqdpFile.GetDefault( gr, slot.IsAccessory(), idTo.Value ), slot, gender, race, idTo.Value );
|
||||
var eqdpFrom = new EqdpManipulation( ExpandedEqdpFile.GetDefault( gr, slotFrom.IsAccessory(), idFrom.Value ), slotFrom, gender, race, idFrom.Value );
|
||||
var eqdpTo = new EqdpManipulation( ExpandedEqdpFile.GetDefault( gr, slotTo.IsAccessory(), idTo.Value ), slotTo, gender, race, idTo.Value );
|
||||
var meta = new MetaSwap( manips, eqdpFrom, eqdpTo );
|
||||
var (ownMtrl, ownMdl) = meta.SwapApplied.Eqdp.Entry.ToBits( slot );
|
||||
var (ownMtrl, ownMdl) = meta.SwapApplied.Eqdp.Entry.ToBits( slotFrom );
|
||||
if( ownMdl )
|
||||
{
|
||||
var mdl = CreateMdl( redirections, slot, gr, idFrom, idTo, mtrlTo );
|
||||
var mdl = CreateMdl( redirections, slotFrom, slotTo, gr, idFrom, idTo, mtrlTo );
|
||||
meta.ChildSwaps.Add( mdl );
|
||||
}
|
||||
else if( !ownMtrl && meta.SwapAppliedIsDefault )
|
||||
|
|
@ -152,15 +218,17 @@ public static class EquipmentSwap
|
|||
}
|
||||
|
||||
public static FileSwap CreateMdl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slot, GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo )
|
||||
=> CreateMdl( redirections, slot, slot, gr, idFrom, idTo, mtrlTo );
|
||||
|
||||
public static FileSwap CreateMdl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo )
|
||||
{
|
||||
var accessory = slot.IsAccessory();
|
||||
var mdlPathFrom = accessory ? GamePaths.Accessory.Mdl.Path( idFrom, gr, slot ) : GamePaths.Equipment.Mdl.Path( idFrom, gr, slot );
|
||||
var mdlPathTo = accessory ? GamePaths.Accessory.Mdl.Path( idTo, gr, slot ) : GamePaths.Equipment.Mdl.Path( idTo, gr, slot );
|
||||
var mdlPathFrom = slotFrom.IsAccessory() ? GamePaths.Accessory.Mdl.Path( idFrom, gr, slotFrom ) : GamePaths.Equipment.Mdl.Path( idFrom, gr, slotFrom );
|
||||
var mdlPathTo = slotTo.IsAccessory() ? GamePaths.Accessory.Mdl.Path( idTo, gr, slotTo ) : GamePaths.Equipment.Mdl.Path( idTo, gr, slotTo );
|
||||
var mdl = FileSwap.CreateSwap( ResourceType.Mdl, redirections, mdlPathFrom, mdlPathTo );
|
||||
|
||||
foreach( ref var fileName in mdl.AsMdl()!.Materials.AsSpan() )
|
||||
{
|
||||
var mtrl = CreateMtrl( redirections, slot, idFrom, idTo, mtrlTo, ref fileName, ref mdl.DataWasChanged );
|
||||
var mtrl = CreateMtrl( redirections, slotFrom, slotTo, idFrom, idTo, mtrlTo, ref fileName, ref mdl.DataWasChanged );
|
||||
if( mtrl != null )
|
||||
{
|
||||
mdl.ChildSwaps.Add( mtrl );
|
||||
|
|
@ -182,22 +250,22 @@ public static class EquipmentSwap
|
|||
variant = ( byte )( ( Quad )i.ModelMain ).B;
|
||||
}
|
||||
|
||||
private static (ImcFile, byte[], Item[]) GetVariants( EquipSlot slot, SetId idFrom, SetId idTo, byte variantFrom )
|
||||
private static (ImcFile, byte[], Item[]) GetVariants( EquipSlot slotFrom, SetId idFrom, SetId idTo, byte variantFrom )
|
||||
{
|
||||
var entry = new ImcManipulation( slot, variantFrom, idFrom.Value, default );
|
||||
var entry = new ImcManipulation( slotFrom, variantFrom, idFrom.Value, default );
|
||||
var imc = new ImcFile( entry );
|
||||
Item[] items;
|
||||
byte[] variants;
|
||||
if( idFrom.Value == idTo.Value )
|
||||
{
|
||||
items = Penumbra.Identifier.Identify( idFrom, variantFrom, slot ).ToArray();
|
||||
items = Penumbra.Identifier.Identify( idFrom, variantFrom, slotFrom ).ToArray();
|
||||
variants = new[] { variantFrom };
|
||||
}
|
||||
else
|
||||
{
|
||||
items = Penumbra.Identifier.Identify( slot.IsEquipment()
|
||||
? GamePaths.Equipment.Mdl.Path( idFrom, GenderRace.MidlanderMale, slot )
|
||||
: GamePaths.Accessory.Mdl.Path( idFrom, GenderRace.MidlanderMale, slot ) ).Select( kvp => kvp.Value ).OfType< Item >().ToArray();
|
||||
items = Penumbra.Identifier.Identify( slotFrom.IsEquipment()
|
||||
? GamePaths.Equipment.Mdl.Path( idFrom, GenderRace.MidlanderMale, slotFrom )
|
||||
: GamePaths.Accessory.Mdl.Path( idFrom, GenderRace.MidlanderMale, slotFrom ) ).Select( kvp => kvp.Value ).OfType< Item >().ToArray();
|
||||
variants = Enumerable.Range( 0, imc.Count + 1 ).Select( i => ( byte )i ).ToArray();
|
||||
}
|
||||
|
||||
|
|
@ -218,11 +286,15 @@ public static class EquipmentSwap
|
|||
|
||||
public static MetaSwap CreateImc( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slot, SetId idFrom, SetId idTo,
|
||||
byte variantFrom, byte variantTo, ImcFile imcFileFrom, ImcFile imcFileTo )
|
||||
=> CreateImc( redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo );
|
||||
|
||||
public static MetaSwap CreateImc( Func< Utf8GamePath, FullPath > redirections, Func< MetaManipulation, MetaManipulation > manips, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo,
|
||||
byte variantFrom, byte variantTo, ImcFile imcFileFrom, ImcFile imcFileTo )
|
||||
{
|
||||
var entryFrom = imcFileFrom.GetEntry( ImcFile.PartIndex( slot ), variantFrom );
|
||||
var entryTo = imcFileTo.GetEntry( ImcFile.PartIndex( slot ), variantTo );
|
||||
var manipulationFrom = new ImcManipulation( slot, variantFrom, idFrom.Value, entryFrom );
|
||||
var manipulationTo = new ImcManipulation( slot, variantTo, idTo.Value, entryTo );
|
||||
var entryFrom = imcFileFrom.GetEntry( ImcFile.PartIndex( slotFrom ), variantFrom );
|
||||
var entryTo = imcFileTo.GetEntry( ImcFile.PartIndex( slotTo ), variantTo );
|
||||
var manipulationFrom = new ImcManipulation( slotFrom, variantFrom, idFrom.Value, entryFrom );
|
||||
var manipulationTo = new ImcManipulation( slotTo, variantTo, idTo.Value, entryTo );
|
||||
var imc = new MetaSwap( manips, manipulationFrom, manipulationTo );
|
||||
|
||||
var decal = CreateDecal( redirections, imc.SwapToModded.Imc.Entry.DecalId );
|
||||
|
|
@ -292,18 +364,23 @@ public static class EquipmentSwap
|
|||
|
||||
public static FileSwap? CreateMtrl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slot, SetId idFrom, SetId idTo, byte variantTo, ref string fileName,
|
||||
ref bool dataWasChanged )
|
||||
=> CreateMtrl( redirections, slot, slot, idFrom, idTo, variantTo, ref fileName, ref dataWasChanged );
|
||||
|
||||
public static FileSwap? CreateMtrl( Func< Utf8GamePath, FullPath > redirections, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo, byte variantTo, ref string fileName,
|
||||
ref bool dataWasChanged )
|
||||
{
|
||||
var prefix = slot.IsAccessory() ? 'a' : 'e';
|
||||
var prefix = slotTo.IsAccessory() ? 'a' : 'e';
|
||||
if( !fileName.Contains( $"{prefix}{idTo.Value:D4}" ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var folderTo = slot.IsAccessory() ? GamePaths.Accessory.Mtrl.FolderPath( idTo, variantTo ) : GamePaths.Equipment.Mtrl.FolderPath( idTo, variantTo );
|
||||
var folderTo = slotTo.IsAccessory() ? GamePaths.Accessory.Mtrl.FolderPath( idTo, variantTo ) : GamePaths.Equipment.Mtrl.FolderPath( idTo, variantTo );
|
||||
var pathTo = $"{folderTo}{fileName}";
|
||||
|
||||
var folderFrom = slot.IsAccessory() ? GamePaths.Accessory.Mtrl.FolderPath( idFrom, variantTo ) : GamePaths.Equipment.Mtrl.FolderPath( idFrom, variantTo );
|
||||
var folderFrom = slotFrom.IsAccessory() ? GamePaths.Accessory.Mtrl.FolderPath( idFrom, variantTo ) : GamePaths.Equipment.Mtrl.FolderPath( idFrom, variantTo );
|
||||
var newFileName = ItemSwap.ReplaceId( fileName, prefix, idTo, idFrom );
|
||||
newFileName = ItemSwap.ReplaceSlot( newFileName, slotTo, slotFrom, slotTo != slotFrom );
|
||||
var pathFrom = $"{folderFrom}{newFileName}";
|
||||
|
||||
if( newFileName != fileName )
|
||||
|
|
@ -318,7 +395,7 @@ public static class EquipmentSwap
|
|||
|
||||
foreach( ref var texture in mtrl.AsMtrl()!.Textures.AsSpan() )
|
||||
{
|
||||
var tex = CreateTex( redirections, prefix, idFrom, idTo, ref texture, ref mtrl.DataWasChanged );
|
||||
var tex = CreateTex( redirections, prefix, slotFrom, slotTo, idFrom, idTo, ref texture, ref mtrl.DataWasChanged );
|
||||
mtrl.ChildSwaps.Add( tex );
|
||||
}
|
||||
|
||||
|
|
@ -326,6 +403,9 @@ public static class EquipmentSwap
|
|||
}
|
||||
|
||||
public static FileSwap CreateTex( Func< Utf8GamePath, FullPath > redirections, char prefix, SetId idFrom, SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged )
|
||||
=> CreateTex( redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref dataWasChanged );
|
||||
|
||||
public static FileSwap CreateTex( Func< Utf8GamePath, FullPath > redirections, char prefix, EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged )
|
||||
{
|
||||
var path = texture.Path;
|
||||
var addedDashes = false;
|
||||
|
|
@ -340,6 +420,7 @@ public static class EquipmentSwap
|
|||
}
|
||||
|
||||
var newPath = ItemSwap.ReplaceAnyId( path, prefix, idFrom );
|
||||
newPath = ItemSwap.ReplaceSlot( newPath, slotTo, slotFrom, slotTo != slotFrom );
|
||||
newPath = ItemSwap.AddSuffix( newPath, ".tex", $"_{Path.GetFileName( texture.Path ).GetStableHashCode():x8}" );
|
||||
if( newPath != path )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,12 +17,6 @@ public static class ItemSwap
|
|||
public class InvalidItemTypeException : Exception
|
||||
{ }
|
||||
|
||||
public class InvalidImcException : Exception
|
||||
{ }
|
||||
|
||||
public class IdUnavailableException : Exception
|
||||
{ }
|
||||
|
||||
public class MissingFileException : Exception
|
||||
{
|
||||
public readonly ResourceType Type;
|
||||
|
|
@ -224,6 +218,11 @@ public static class ItemSwap
|
|||
? path.Replace( $"{type}{idFrom.Value:D4}", $"{type}{idTo.Value:D4}" )
|
||||
: path;
|
||||
|
||||
public static string ReplaceSlot( string path, EquipSlot from, EquipSlot to, bool condition = true )
|
||||
=> condition
|
||||
? path.Replace( $"_{from.ToSuffix()}_", $"_{to.ToSuffix()}_" )
|
||||
: path;
|
||||
|
||||
public static string ReplaceRace( string path, GenderRace from, GenderRace to, bool condition = true )
|
||||
=> ReplaceId( path, 'c', ( ushort )from, ( ushort )to, condition );
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
using Penumbra.String.Classes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Penumbra.Mods.ItemSwap;
|
||||
|
||||
|
|
@ -133,6 +133,15 @@ public class ItemSwapContainer
|
|||
return ret;
|
||||
}
|
||||
|
||||
public Item[] LoadTypeSwap( EquipSlot slotFrom, Item from, EquipSlot slotTo, Item to, ModCollection? collection = null )
|
||||
{
|
||||
Swaps.Clear();
|
||||
Loaded = false;
|
||||
var ret = EquipmentSwap.CreateTypeSwap( Swaps, PathResolver( collection ), MetaResolver( collection ), slotFrom, from, slotTo, to );
|
||||
Loaded = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool LoadCustomization( BodySlot slot, GenderRace race, SetId from, SetId to, ModCollection? collection = null )
|
||||
{
|
||||
var pathResolver = PathResolver( collection );
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ public class ItemSwapWindow : IDisposable
|
|||
Necklace,
|
||||
Bracelet,
|
||||
Ring,
|
||||
BetweenSlots,
|
||||
Hair,
|
||||
Face,
|
||||
Ears,
|
||||
|
|
@ -101,6 +102,8 @@ public class ItemSwapWindow : IDisposable
|
|||
private int _targetId = 0;
|
||||
private int _sourceId = 0;
|
||||
private Exception? _loadException = null;
|
||||
private EquipSlot _slotFrom = EquipSlot.Head;
|
||||
private EquipSlot _slotTo = EquipSlot.Ears;
|
||||
|
||||
private string _newModName = string.Empty;
|
||||
private string _newGroupName = "Swaps";
|
||||
|
|
@ -164,6 +167,15 @@ public class ItemSwapWindow : IDisposable
|
|||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null, _useRightRing, _useLeftRing );
|
||||
}
|
||||
|
||||
break;
|
||||
case SwapType.BetweenSlots:
|
||||
var (_, _, selectorFrom) = GetAccessorySelector( _slotFrom, true );
|
||||
var (_, _, selectorTo) = GetAccessorySelector( _slotTo, false );
|
||||
if( selectorFrom.CurrentSelection.Item2 != null && selectorTo.CurrentSelection.Item2 != null )
|
||||
{
|
||||
_affectedItems = _swapData.LoadTypeSwap( _slotTo, selectorTo.CurrentSelection.Item2, _slotFrom, selectorFrom.CurrentSelection.Item2,
|
||||
_useCurrentCollection ? Penumbra.CollectionManager.Current : null);
|
||||
}
|
||||
break;
|
||||
case SwapType.Hair when _targetId > 0 && _sourceId > 0:
|
||||
_swapData.LoadCustomization( BodySlot.Hair, Names.CombinedRace( _currentGender, _currentRace ), ( SetId )_sourceId, ( SetId )_targetId,
|
||||
|
|
@ -364,6 +376,7 @@ public class ItemSwapWindow : IDisposable
|
|||
DrawEquipmentSwap( SwapType.Necklace );
|
||||
DrawEquipmentSwap( SwapType.Bracelet );
|
||||
DrawEquipmentSwap( SwapType.Ring );
|
||||
DrawAccessorySwap();
|
||||
DrawHairSwap();
|
||||
DrawFaceSwap();
|
||||
DrawEarSwap();
|
||||
|
|
@ -373,7 +386,7 @@ public class ItemSwapWindow : IDisposable
|
|||
|
||||
private ImRaii.IEndObject DrawTab( SwapType newTab )
|
||||
{
|
||||
using var tab = ImRaii.TabItem( newTab.ToString() );
|
||||
using var tab = ImRaii.TabItem( newTab is SwapType.BetweenSlots ? "Between Slots" : newTab.ToString() );
|
||||
if( tab )
|
||||
{
|
||||
_dirty |= _lastTab != newTab;
|
||||
|
|
@ -385,6 +398,99 @@ public class ItemSwapWindow : IDisposable
|
|||
return tab;
|
||||
}
|
||||
|
||||
private void DrawAccessorySwap()
|
||||
{
|
||||
using var tab = DrawTab( SwapType.BetweenSlots );
|
||||
if( !tab )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var table = ImRaii.Table( "##settings", 3, ImGuiTableFlags.SizingFixedFit );
|
||||
ImGui.TableSetupColumn( "##text", ImGuiTableColumnFlags.WidthFixed, ImGui.CalcTextSize( "and put them on these" ).X );
|
||||
|
||||
var (article1, article2, selector) = GetAccessorySelector( _slotFrom, true );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( $"Take {article1}" );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale );
|
||||
using( var combo = ImRaii.Combo( "##fromType", _slotFrom is EquipSlot.Head ? "Hat" : _slotFrom.ToName() ) )
|
||||
{
|
||||
if( combo )
|
||||
{
|
||||
foreach( var slot in EquipSlotExtensions.AccessorySlots.Prepend(EquipSlot.Head) )
|
||||
{
|
||||
if( ImGui.Selectable( slot is EquipSlot.Head ? "Hat" : slot.ToName(), slot == _slotFrom ) && slot != _slotFrom )
|
||||
{
|
||||
_dirty = true;
|
||||
_slotFrom = slot;
|
||||
if( slot == _slotTo )
|
||||
{
|
||||
_slotTo = EquipSlotExtensions.AccessorySlots.First( s => slot != s );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
_dirty |= selector.Draw( "##itemSource", selector.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2, ImGui.GetTextLineHeightWithSpacing() );
|
||||
|
||||
(article1, _, selector) = GetAccessorySelector( _slotTo, false );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted( $"and put {article2} on {article1}" );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale );
|
||||
using( var combo = ImRaii.Combo( "##toType", _slotTo.ToName() ) )
|
||||
{
|
||||
if( combo )
|
||||
{
|
||||
foreach( var slot in EquipSlotExtensions.AccessorySlots.Where( s => s != _slotFrom ) )
|
||||
{
|
||||
if( ImGui.Selectable( slot.ToName(), slot == _slotTo ) && slot != _slotTo )
|
||||
{
|
||||
_dirty = true;
|
||||
_slotTo = slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
_dirty |= selector.Draw( "##itemTarget", selector.CurrentSelection.Item1 ?? string.Empty, string.Empty, InputWidth * 2, ImGui.GetTextLineHeightWithSpacing() );
|
||||
if( _affectedItems is { Length: > 1 } )
|
||||
{
|
||||
ImGui.SameLine();
|
||||
ImGuiUtil.DrawTextButton( $"which will also affect {_affectedItems.Length - 1} other Items.", Vector2.Zero, Colors.PressEnterWarningBg );
|
||||
if( ImGui.IsItemHovered() )
|
||||
{
|
||||
ImGui.SetTooltip( string.Join( '\n', _affectedItems.Where( i => !ReferenceEquals( i, selector.CurrentSelection.Item2 ) )
|
||||
.Select( i => i.Name.ToDalamudString().TextValue ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private (string, string, ItemSelector) GetAccessorySelector( EquipSlot slot, bool source )
|
||||
{
|
||||
var (type, article1, article2) = slot switch
|
||||
{
|
||||
EquipSlot.Head => (SwapType.Hat, "this", "it"),
|
||||
EquipSlot.Ears => (SwapType.Earrings, "these", "them"),
|
||||
EquipSlot.Neck => (SwapType.Necklace, "this", "it"),
|
||||
EquipSlot.Wrists => (SwapType.Bracelet, "these", "them"),
|
||||
EquipSlot.RFinger => (SwapType.Ring, "this", "it"),
|
||||
EquipSlot.LFinger => (SwapType.Ring, "this", "it"),
|
||||
_ => (SwapType.Ring, "this", "it"),
|
||||
};
|
||||
var tuple = _selectors[ type ];
|
||||
return (article1, article2, source ? tuple.Source : tuple.Target);
|
||||
}
|
||||
|
||||
private void DrawEquipmentSwap( SwapType type )
|
||||
{
|
||||
using var tab = DrawTab( type );
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue