mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-01-02 05:43:42 +01:00
.
This commit is contained in:
parent
7710cfadfa
commit
2d6fd6015d
88 changed files with 2304 additions and 383 deletions
320
GlamourerOld/Designs/Design.cs
Normal file
320
GlamourerOld/Designs/Design.cs
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public partial class Design : DesignData, ISavable
|
||||
{
|
||||
public const int FileVersion = 1;
|
||||
|
||||
|
||||
public Guid Identifier { get; internal init; }
|
||||
public DateTimeOffset CreationDate { get; internal init; }
|
||||
public LowerString Name { get; internal set; } = LowerString.Empty;
|
||||
public string Description { get; internal set; } = string.Empty;
|
||||
public string[] Tags { get; internal set; } = Array.Empty<string>();
|
||||
public int Index { get; internal set; }
|
||||
|
||||
public EquipFlag ApplyEquip { get; internal set; }
|
||||
public CustomizeFlag ApplyCustomize { get; internal set; }
|
||||
public QuadBool Wetness { get; internal set; } = QuadBool.NullFalse;
|
||||
public QuadBool Visor { get; internal set; } = QuadBool.NullFalse;
|
||||
public QuadBool Hat { get; internal set; } = QuadBool.NullFalse;
|
||||
public QuadBool Weapon { get; internal set; } = QuadBool.NullFalse;
|
||||
public bool WriteProtected { get; internal set; }
|
||||
|
||||
public bool DoApplyEquip(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToFlag());
|
||||
|
||||
public bool DoApplyStain(EquipSlot slot)
|
||||
=> ApplyEquip.HasFlag(slot.ToStainFlag());
|
||||
|
||||
public bool DoApplyCustomize(CustomizeIndex idx)
|
||||
=> ApplyCustomize.HasFlag(idx.ToFlag());
|
||||
|
||||
internal bool SetApplyEquip(EquipSlot slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyEquip | slot.ToFlag() : ApplyEquip & ~slot.ToFlag();
|
||||
if (newValue == ApplyEquip)
|
||||
return false;
|
||||
|
||||
ApplyEquip = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyStain(EquipSlot slot, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyEquip | slot.ToStainFlag() : ApplyEquip & ~slot.ToStainFlag();
|
||||
if (newValue == ApplyEquip)
|
||||
return false;
|
||||
|
||||
ApplyEquip = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetApplyCustomize(CustomizeIndex idx, bool value)
|
||||
{
|
||||
var newValue = value ? ApplyCustomize | idx.ToFlag() : ApplyCustomize & ~idx.ToFlag();
|
||||
if (newValue == ApplyCustomize)
|
||||
return false;
|
||||
|
||||
ApplyCustomize = newValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
internal Design(ItemManager items)
|
||||
: base(items)
|
||||
{ }
|
||||
|
||||
public JObject JsonSerialize()
|
||||
{
|
||||
var ret = new JObject
|
||||
{
|
||||
[nameof(FileVersion)] = FileVersion,
|
||||
[nameof(Identifier)] = Identifier,
|
||||
[nameof(CreationDate)] = CreationDate,
|
||||
[nameof(Name)] = Name.Text,
|
||||
[nameof(Description)] = Description,
|
||||
[nameof(Tags)] = JArray.FromObject(Tags),
|
||||
[nameof(WriteProtected)] = WriteProtected,
|
||||
["Equipment"] = SerializeEquipment(),
|
||||
[nameof(ModelData.Customize)] = SerializeCustomize(),
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
public JObject SerializeEquipment()
|
||||
{
|
||||
static JObject Serialize(uint itemId, StainId stain, bool apply, bool applyStain)
|
||||
=> new()
|
||||
{
|
||||
[nameof(Item.ItemId)] = itemId,
|
||||
[nameof(Item.Stain)] = stain.Value,
|
||||
["Apply"] = apply,
|
||||
["ApplyStain"] = applyStain,
|
||||
};
|
||||
|
||||
var ret = new JObject()
|
||||
{
|
||||
[nameof(MainHandId)] =
|
||||
Serialize(MainHandId, ModelData.MainHand.Stain, DoApplyEquip(EquipSlot.MainHand), DoApplyStain(EquipSlot.MainHand)),
|
||||
[nameof(OffHandId)] = Serialize(OffHandId, ModelData.OffHand.Stain, DoApplyEquip(EquipSlot.OffHand),
|
||||
DoApplyStain(EquipSlot.OffHand)),
|
||||
};
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var armor = Armor(slot);
|
||||
ret[slot.ToString()] = Serialize(armor.ItemId, armor.Stain, DoApplyEquip(slot), DoApplyStain(slot));
|
||||
}
|
||||
|
||||
ret[nameof(Hat)] = Hat.ToJObject("Show", "Apply");
|
||||
ret[nameof(Weapon)] = Weapon.ToJObject("Show", "Apply");
|
||||
ret[nameof(Visor)] = Visor.ToJObject("IsToggled", "Apply");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public JObject SerializeCustomize()
|
||||
{
|
||||
var ret = new JObject()
|
||||
{
|
||||
[nameof(ModelId)] = ModelId,
|
||||
};
|
||||
var customize = ModelData.Customize;
|
||||
foreach (var idx in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
var data = customize[idx];
|
||||
ret[idx.ToString()] = new JObject()
|
||||
{
|
||||
["Value"] = data.Value,
|
||||
["Apply"] = true,
|
||||
};
|
||||
}
|
||||
|
||||
ret[nameof(Wetness)] = Wetness.ToJObject("IsWet", "Apply");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Design LoadDesign(ItemManager items, JObject json, out bool changes)
|
||||
{
|
||||
var version = json[nameof(FileVersion)]?.ToObject<int>() ?? 0;
|
||||
return version switch
|
||||
{
|
||||
1 => LoadDesignV1(items, json, out changes),
|
||||
_ => throw new Exception("The design to be loaded has no valid Version."),
|
||||
};
|
||||
}
|
||||
|
||||
internal static Design LoadDesignV1(ItemManager items, JObject json, out bool changes)
|
||||
{
|
||||
static string[] ParseTags(JObject json)
|
||||
{
|
||||
var tags = json["Tags"]?.ToObject<string[]>() ?? Array.Empty<string>();
|
||||
return tags.OrderBy(t => t).Distinct().ToArray();
|
||||
}
|
||||
|
||||
var design = new Design(items)
|
||||
{
|
||||
CreationDate = json["CreationDate"]?.ToObject<DateTimeOffset>() ?? throw new ArgumentNullException("CreationDate"),
|
||||
Identifier = json["Identifier"]?.ToObject<Guid>() ?? throw new ArgumentNullException("Identifier"),
|
||||
Name = new LowerString(json["Name"]?.ToObject<string>() ?? throw new ArgumentNullException("Name")),
|
||||
Description = json["Description"]?.ToObject<string>() ?? string.Empty,
|
||||
Tags = ParseTags(json),
|
||||
};
|
||||
|
||||
changes = LoadEquip(items, json["Equipment"], design);
|
||||
changes |= LoadCustomize(json["Customize"], design);
|
||||
return design;
|
||||
}
|
||||
|
||||
internal static bool LoadEquip(ItemManager items, JToken? equip, Design design)
|
||||
{
|
||||
if (equip == null)
|
||||
return true;
|
||||
|
||||
static (uint, StainId, bool, bool) ParseItem(EquipSlot slot, JToken? item)
|
||||
{
|
||||
var id = item?["ItemId"]?.ToObject<uint>() ?? ItemManager.NothingId(slot);
|
||||
var stain = (StainId)(item?["Stain"]?.ToObject<byte>() ?? 0);
|
||||
var apply = item?["Apply"]?.ToObject<bool>() ?? false;
|
||||
var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false;
|
||||
return (id, stain, apply, applyStain);
|
||||
}
|
||||
|
||||
var changes = false;
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var (id, stain, apply, applyStain) = ParseItem(slot, equip[slot.ToString()]);
|
||||
changes |= !design.SetArmor(items, slot, id);
|
||||
changes |= !design.SetStain(slot, stain);
|
||||
design.SetApplyEquip(slot, apply);
|
||||
design.SetApplyStain(slot, applyStain);
|
||||
}
|
||||
|
||||
var main = equip["MainHand"];
|
||||
if (main == null)
|
||||
{
|
||||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = main["ItemId"]?.ToObject<uint>() ?? items.DefaultSword.RowId;
|
||||
var stain = (StainId)(main["Stain"]?.ToObject<byte>() ?? 0);
|
||||
var apply = main["Apply"]?.ToObject<bool>() ?? false;
|
||||
var applyStain = main["ApplyStain"]?.ToObject<bool>() ?? false;
|
||||
changes |= !design.SetMainhand(items, id);
|
||||
changes |= !design.SetStain(EquipSlot.MainHand, stain);
|
||||
design.SetApplyEquip(EquipSlot.MainHand, apply);
|
||||
design.SetApplyStain(EquipSlot.MainHand, applyStain);
|
||||
}
|
||||
|
||||
var off = equip["OffHand"];
|
||||
if (off == null)
|
||||
{
|
||||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = off["ItemId"]?.ToObject<uint>() ?? ItemManager.NothingId(design.MainhandType.Offhand());
|
||||
var stain = (StainId)(off["Stain"]?.ToObject<byte>() ?? 0);
|
||||
var apply = off["Apply"]?.ToObject<bool>() ?? false;
|
||||
var applyStain = off["ApplyStain"]?.ToObject<bool>() ?? false;
|
||||
changes |= !design.SetOffhand(items, id);
|
||||
changes |= !design.SetStain(EquipSlot.OffHand, stain);
|
||||
design.SetApplyEquip(EquipSlot.OffHand, apply);
|
||||
design.SetApplyStain(EquipSlot.OffHand, applyStain);
|
||||
}
|
||||
|
||||
design.Hat = QuadBool.FromJObject(equip["Hat"], "Show", "Apply", QuadBool.NullFalse);
|
||||
design.Weapon = QuadBool.FromJObject(equip["Weapon"], "Show", "Apply", QuadBool.NullFalse);
|
||||
design.Visor = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
internal static bool LoadCustomize(JToken? json, Design design)
|
||||
{
|
||||
if (json == null)
|
||||
return true;
|
||||
|
||||
ref var customize = ref design.ModelData.Customize;
|
||||
foreach (var idx in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
var tok = json[idx.ToString()];
|
||||
var data = (CustomizeValue)(tok?["Value"]?.ToObject<byte>() ?? 0);
|
||||
var apply = tok?["Apply"]?.ToObject<bool>() ?? false;
|
||||
customize[idx] = data;
|
||||
design.SetApplyCustomize(idx, apply);
|
||||
}
|
||||
|
||||
design.Wetness = QuadBool.FromJObject(json["Wetness"], "IsWet", "Apply", QuadBool.NullFalse);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void MigrateBase64(ItemManager items, string base64)
|
||||
{
|
||||
var data = DesignBase64Migration.MigrateBase64(base64, out var applyEquip, out var applyCustomize, out var writeProtected, out var wet,
|
||||
out var hat,
|
||||
out var visor, out var weapon);
|
||||
UpdateMainhand(items, data.MainHand);
|
||||
UpdateOffhand(items, data.OffHand);
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
UpdateArmor(items, slot, data.Armor(slot), true);
|
||||
ModelData.Customize = data.Customize;
|
||||
ApplyEquip = applyEquip;
|
||||
ApplyCustomize = applyCustomize;
|
||||
WriteProtected = writeProtected;
|
||||
Wetness = wet;
|
||||
Hat = hat;
|
||||
Visor = visor;
|
||||
Weapon = weapon;
|
||||
}
|
||||
|
||||
public static Design CreateTemporaryFromBase64(ItemManager items, string base64, bool customize, bool equip)
|
||||
{
|
||||
var ret = new Design(items);
|
||||
ret.MigrateBase64(items, base64);
|
||||
if (!customize)
|
||||
ret.ApplyCustomize = 0;
|
||||
if (!equip)
|
||||
ret.ApplyEquip = 0;
|
||||
ret.Wetness = ret.Wetness.SetEnabled(customize);
|
||||
ret.Visor = ret.Visor.SetEnabled(equip);
|
||||
ret.Hat = ret.Hat.SetEnabled(equip);
|
||||
ret.Weapon = ret.Weapon.SetEnabled(equip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Outdated.
|
||||
public string CreateOldBase64()
|
||||
=> DesignBase64Migration.CreateOldBase64(in ModelData, ApplyEquip, ApplyCustomize, Wetness == QuadBool.True, Hat.ForcedValue,
|
||||
Hat.Enabled,
|
||||
Visor.ForcedValue, Visor.Enabled, Weapon.ForcedValue, Weapon.Enabled, WriteProtected, 1f);
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.DesignFile(this);
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
using var j = new JsonTextWriter(writer)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
};
|
||||
var obj = JsonSerialize();
|
||||
obj.WriteTo(j);
|
||||
}
|
||||
|
||||
public string LogName(string fileName)
|
||||
=> Path.GetFileNameWithoutExtension(fileName);
|
||||
}
|
||||
430
GlamourerOld/Designs/DesignData.cs
Normal file
430
GlamourerOld/Designs/DesignData.cs
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public class DesignData
|
||||
{
|
||||
internal ModelData ModelData;
|
||||
public FullEquipType MainhandType { get; internal set; }
|
||||
|
||||
public uint Head = ItemManager.NothingId(EquipSlot.Head);
|
||||
public uint Body = ItemManager.NothingId(EquipSlot.Body);
|
||||
public uint Hands = ItemManager.NothingId(EquipSlot.Hands);
|
||||
public uint Legs = ItemManager.NothingId(EquipSlot.Legs);
|
||||
public uint Feet = ItemManager.NothingId(EquipSlot.Feet);
|
||||
public uint Ears = ItemManager.NothingId(EquipSlot.Ears);
|
||||
public uint Neck = ItemManager.NothingId(EquipSlot.Neck);
|
||||
public uint Wrists = ItemManager.NothingId(EquipSlot.Wrists);
|
||||
public uint RFinger = ItemManager.NothingId(EquipSlot.RFinger);
|
||||
public uint LFinger = ItemManager.NothingId(EquipSlot.RFinger);
|
||||
public uint MainHandId;
|
||||
public uint OffHandId;
|
||||
|
||||
public string HeadName = ItemManager.Nothing;
|
||||
public string BodyName = ItemManager.Nothing;
|
||||
public string HandsName = ItemManager.Nothing;
|
||||
public string LegsName = ItemManager.Nothing;
|
||||
public string FeetName = ItemManager.Nothing;
|
||||
public string EarsName = ItemManager.Nothing;
|
||||
public string NeckName = ItemManager.Nothing;
|
||||
public string WristsName = ItemManager.Nothing;
|
||||
public string RFingerName = ItemManager.Nothing;
|
||||
public string LFingerName = ItemManager.Nothing;
|
||||
public string MainhandName;
|
||||
public string OffhandName;
|
||||
|
||||
public DesignData(ItemManager items)
|
||||
{
|
||||
MainHandId = items.DefaultSword.RowId;
|
||||
(_, var set, var type, var variant, MainhandName, MainhandType) = items.Resolve(MainHandId, items.DefaultSword);
|
||||
|
||||
ModelData = new ModelData(new CharacterWeapon(set, type, variant, 0));
|
||||
OffHandId = ItemManager.NothingId(MainhandType.Offhand());
|
||||
(_, ModelData.OffHand.Set, ModelData.OffHand.Type, ModelData.OffHand.Variant, OffhandName, _) =
|
||||
items.Resolve(OffHandId, MainhandType);
|
||||
}
|
||||
|
||||
public uint ModelId
|
||||
=> ModelData.ModelId;
|
||||
|
||||
public Item Armor(EquipSlot slot)
|
||||
{
|
||||
return slot switch
|
||||
{
|
||||
EquipSlot.Head => new Item(HeadName, Head, ModelData.Head),
|
||||
EquipSlot.Body => new Item(BodyName, Body, ModelData.Body),
|
||||
EquipSlot.Hands => new Item(HandsName, Hands, ModelData.Hands),
|
||||
EquipSlot.Legs => new Item(LegsName, Legs, ModelData.Legs),
|
||||
EquipSlot.Feet => new Item(FeetName, Feet, ModelData.Feet),
|
||||
EquipSlot.Ears => new Item(EarsName, Ears, ModelData.Ears),
|
||||
EquipSlot.Neck => new Item(NeckName, Neck, ModelData.Neck),
|
||||
EquipSlot.Wrists => new Item(WristsName, Wrists, ModelData.Wrists),
|
||||
EquipSlot.RFinger => new Item(RFingerName, RFinger, ModelData.RFinger),
|
||||
EquipSlot.LFinger => new Item(LFingerName, LFinger, ModelData.LFinger),
|
||||
_ => throw new Exception("Invalid equip slot for item."),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public Weapon WeaponMain
|
||||
=> new(MainhandName, MainHandId, ModelData.MainHand, MainhandType);
|
||||
|
||||
public Weapon WeaponOff
|
||||
=> Weapon.Offhand(OffhandName, OffHandId, ModelData.OffHand, MainhandType);
|
||||
|
||||
public CustomizeValue GetCustomize(CustomizeIndex idx)
|
||||
=> ModelData.Customize[idx];
|
||||
|
||||
internal bool SetCustomize(CustomizeIndex idx, CustomizeValue value)
|
||||
=> ModelData.Customize.Set(idx, value);
|
||||
|
||||
internal bool SetArmor(ItemManager items, EquipSlot slot, uint itemId, Lumina.Excel.GeneratedSheets.Item? item = null)
|
||||
{
|
||||
var (valid, set, variant, name) = items.Resolve(slot, itemId, item);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
return SetArmor(slot, set, variant, name, itemId);
|
||||
}
|
||||
|
||||
internal bool SetArmor(EquipSlot slot, Item item)
|
||||
=> SetArmor(slot, item.ModelBase, item.Variant, item.Name, item.ItemId);
|
||||
|
||||
internal bool UpdateArmor(ItemManager items, EquipSlot slot, CharacterArmor armor, bool force)
|
||||
{
|
||||
var (valid, id, name) = items.Identify(slot, armor.Set, armor.Variant);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
return SetArmor(slot, armor.Set, armor.Variant, name, id) | SetStain(slot, armor.Stain);
|
||||
}
|
||||
|
||||
internal bool SetMainhand(ItemManager items, uint mainId, Lumina.Excel.GeneratedSheets.Item? main = null)
|
||||
{
|
||||
if (mainId == MainHandId)
|
||||
return false;
|
||||
|
||||
var (valid, set, weapon, variant, name, type) = items.Resolve(mainId, main);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
var fixOffhand = type.Offhand() != MainhandType.Offhand();
|
||||
|
||||
MainHandId = mainId;
|
||||
MainhandName = name;
|
||||
MainhandType = type;
|
||||
ModelData.MainHand.Set = set;
|
||||
ModelData.MainHand.Type = weapon;
|
||||
ModelData.MainHand.Variant = variant;
|
||||
if (fixOffhand)
|
||||
SetOffhand(items, ItemManager.NothingId(type.Offhand()));
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetOffhand(ItemManager items, uint offId, Lumina.Excel.GeneratedSheets.Item? off = null)
|
||||
{
|
||||
if (offId == OffHandId)
|
||||
return false;
|
||||
|
||||
var (valid, set, weapon, variant, name, type) = items.Resolve(offId, MainhandType, off);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
OffHandId = offId;
|
||||
OffhandName = name;
|
||||
ModelData.OffHand.Set = set;
|
||||
ModelData.OffHand.Type = weapon;
|
||||
ModelData.OffHand.Variant = variant;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool UpdateMainhand(ItemManager items, CharacterWeapon weapon)
|
||||
{
|
||||
if (weapon.Value == ModelData.MainHand.Value)
|
||||
return false;
|
||||
|
||||
var (valid, id, name, type) = items.Identify(EquipSlot.MainHand, weapon.Set, weapon.Type, (byte)weapon.Variant);
|
||||
if (!valid || id == MainHandId)
|
||||
return false;
|
||||
|
||||
var fixOffhand = type.Offhand() != MainhandType.Offhand();
|
||||
|
||||
MainHandId = id;
|
||||
MainhandName = name;
|
||||
MainhandType = type;
|
||||
ModelData.MainHand.Set = weapon.Set;
|
||||
ModelData.MainHand.Type = weapon.Type;
|
||||
ModelData.MainHand.Variant = weapon.Variant;
|
||||
ModelData.MainHand.Stain = weapon.Stain;
|
||||
if (fixOffhand)
|
||||
SetOffhand(items, ItemManager.NothingId(type.Offhand()));
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool UpdateOffhand(ItemManager items, CharacterWeapon weapon)
|
||||
{
|
||||
if (weapon.Value == ModelData.OffHand.Value)
|
||||
return false;
|
||||
|
||||
var (valid, id, name, _) = items.Identify(EquipSlot.OffHand, weapon.Set, weapon.Type, (byte)weapon.Variant, MainhandType);
|
||||
if (!valid || id == OffHandId)
|
||||
return false;
|
||||
|
||||
OffHandId = id;
|
||||
OffhandName = name;
|
||||
ModelData.OffHand.Set = weapon.Set;
|
||||
ModelData.OffHand.Type = weapon.Type;
|
||||
ModelData.OffHand.Variant = weapon.Variant;
|
||||
ModelData.OffHand.Stain = weapon.Stain;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SetStain(EquipSlot slot, StainId id)
|
||||
{
|
||||
return slot switch
|
||||
{
|
||||
EquipSlot.MainHand => SetIfDifferent(ref ModelData.MainHand.Stain, id),
|
||||
EquipSlot.OffHand => SetIfDifferent(ref ModelData.OffHand.Stain, id),
|
||||
EquipSlot.Head => SetIfDifferent(ref ModelData.Head.Stain, id),
|
||||
EquipSlot.Body => SetIfDifferent(ref ModelData.Body.Stain, id),
|
||||
EquipSlot.Hands => SetIfDifferent(ref ModelData.Hands.Stain, id),
|
||||
EquipSlot.Legs => SetIfDifferent(ref ModelData.Legs.Stain, id),
|
||||
EquipSlot.Feet => SetIfDifferent(ref ModelData.Feet.Stain, id),
|
||||
EquipSlot.Ears => SetIfDifferent(ref ModelData.Ears.Stain, id),
|
||||
EquipSlot.Neck => SetIfDifferent(ref ModelData.Neck.Stain, id),
|
||||
EquipSlot.Wrists => SetIfDifferent(ref ModelData.Wrists.Stain, id),
|
||||
EquipSlot.RFinger => SetIfDifferent(ref ModelData.RFinger.Stain, id),
|
||||
EquipSlot.LFinger => SetIfDifferent(ref ModelData.LFinger.Stain, id),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
internal static bool SetIfDifferent<T>(ref T old, T value) where T : IEquatable<T>
|
||||
{
|
||||
if (old.Equals(value))
|
||||
return false;
|
||||
|
||||
old = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool SetArmor(EquipSlot slot, SetId set, byte variant, string name, uint id)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref ModelData.Head.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Head.Variant, variant);
|
||||
changes |= HeadName != name;
|
||||
HeadName = name;
|
||||
changes |= Head != id;
|
||||
Head = id;
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref ModelData.Body.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Body.Variant, variant);
|
||||
changes |= BodyName != name;
|
||||
BodyName = name;
|
||||
changes |= Body != id;
|
||||
Body = id;
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref ModelData.Hands.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Hands.Variant, variant);
|
||||
changes |= HandsName != name;
|
||||
HandsName = name;
|
||||
changes |= Hands != id;
|
||||
Hands = id;
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref ModelData.Legs.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Legs.Variant, variant);
|
||||
changes |= LegsName != name;
|
||||
LegsName = name;
|
||||
changes |= Legs != id;
|
||||
Legs = id;
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref ModelData.Feet.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Feet.Variant, variant);
|
||||
changes |= FeetName != name;
|
||||
FeetName = name;
|
||||
changes |= Feet != id;
|
||||
Feet = id;
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref ModelData.Ears.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Ears.Variant, variant);
|
||||
changes |= EarsName != name;
|
||||
EarsName = name;
|
||||
changes |= Ears != id;
|
||||
Ears = id;
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref ModelData.Neck.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Neck.Variant, variant);
|
||||
changes |= NeckName != name;
|
||||
NeckName = name;
|
||||
changes |= Neck != id;
|
||||
Neck = id;
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref ModelData.Wrists.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.Wrists.Variant, variant);
|
||||
changes |= WristsName != name;
|
||||
WristsName = name;
|
||||
changes |= Wrists != id;
|
||||
Wrists = id;
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref ModelData.RFinger.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.RFinger.Variant, variant);
|
||||
changes |= RFingerName != name;
|
||||
RFingerName = name;
|
||||
changes |= RFinger != id;
|
||||
RFinger = id;
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref ModelData.LFinger.Set, set);
|
||||
changes |= SetIfDifferent(ref ModelData.LFinger.Variant, variant);
|
||||
changes |= LFingerName != name;
|
||||
LFingerName = name;
|
||||
changes |= LFinger != id;
|
||||
LFinger = id;
|
||||
return changes;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DesignBase64Migration
|
||||
{
|
||||
public const int Base64Size = 91;
|
||||
|
||||
public static ModelData MigrateBase64(string base64, out EquipFlag equipFlags, out CustomizeFlag customizeFlags,
|
||||
out bool writeinternal, out QuadBool wet, out QuadBool hat, out QuadBool visor, out QuadBool weapon)
|
||||
{
|
||||
static void CheckSize(int length, int requiredLength)
|
||||
{
|
||||
if (length != requiredLength)
|
||||
throw new Exception(
|
||||
$"Can not parse Base64 string into CharacterSave:\n\tInvalid size {length} instead of {requiredLength}.");
|
||||
}
|
||||
|
||||
byte applicationFlags;
|
||||
ushort equipFlagsS;
|
||||
var bytes = Convert.FromBase64String(base64);
|
||||
hat = QuadBool.Null;
|
||||
visor = QuadBool.Null;
|
||||
weapon = QuadBool.Null;
|
||||
switch (bytes[0])
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
CheckSize(bytes.Length, 86);
|
||||
applicationFlags = bytes[1];
|
||||
equipFlagsS = BitConverter.ToUInt16(bytes, 2);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
CheckSize(bytes.Length, Base64Size);
|
||||
applicationFlags = bytes[1];
|
||||
equipFlagsS = BitConverter.ToUInt16(bytes, 2);
|
||||
hat = hat.SetValue((bytes[90] & 0x01) == 0);
|
||||
visor = visor.SetValue((bytes[90] & 0x10) != 0);
|
||||
weapon = weapon.SetValue((bytes[90] & 0x02) == 0);
|
||||
break;
|
||||
}
|
||||
default: throw new Exception($"Can not parse Base64 string into design for migration:\n\tInvalid Version {bytes[0]}.");
|
||||
}
|
||||
|
||||
customizeFlags = (applicationFlags & 0x01) != 0 ? CustomizeFlagExtensions.All : 0;
|
||||
wet = (applicationFlags & 0x02) != 0 ? QuadBool.True : QuadBool.NullFalse;
|
||||
hat = hat.SetEnabled((applicationFlags & 0x04) != 0);
|
||||
weapon = weapon.SetEnabled((applicationFlags & 0x08) != 0);
|
||||
visor = visor.SetEnabled((applicationFlags & 0x10) != 0);
|
||||
writeinternal = (applicationFlags & 0x20) != 0;
|
||||
|
||||
equipFlags = 0;
|
||||
equipFlags |= (equipFlagsS & 0x0001) != 0 ? EquipFlag.Mainhand | EquipFlag.MainhandStain : 0;
|
||||
equipFlags |= (equipFlagsS & 0x0002) != 0 ? EquipFlag.Offhand | EquipFlag.OffhandStain : 0;
|
||||
var flag = 0x0002u;
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
flag <<= 1;
|
||||
equipFlags |= (equipFlagsS & flag) != 0 ? slot.ToFlag() | slot.ToStainFlag() : 0;
|
||||
}
|
||||
|
||||
var data = new ModelData();
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* ptr = bytes)
|
||||
{
|
||||
data.Customize.Load(*(Customize*) (ptr + 4));
|
||||
var cur = (CharacterWeapon*)(ptr + 30);
|
||||
data.MainHand = cur[0];
|
||||
data.OffHand = cur[1];
|
||||
var eq = (CharacterArmor*)(cur + 2);
|
||||
foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex())
|
||||
{
|
||||
var mdl = eq[idx];
|
||||
data.SetPiece(slot, mdl.Set, mdl.Variant, mdl.Stain, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static unsafe string CreateOldBase64(in ModelData save, EquipFlag equipFlags, CustomizeFlag customizeFlags, bool wet, bool hat,
|
||||
bool setHat, bool visor, bool setVisor, bool weapon, bool setWeapon, bool writeinternal, float alpha)
|
||||
{
|
||||
var data = stackalloc byte[Base64Size];
|
||||
data[0] = 2;
|
||||
data[1] = (byte)((customizeFlags == CustomizeFlagExtensions.All ? 0x01 : 0)
|
||||
| (wet ? 0x02 : 0)
|
||||
| (setHat ? 0x04 : 0)
|
||||
| (setWeapon ? 0x08 : 0)
|
||||
| (setVisor ? 0x10 : 0)
|
||||
| (writeinternal ? 0x20 : 0));
|
||||
data[2] = (byte)((equipFlags.HasFlag(EquipFlag.Mainhand) ? 0x01 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Offhand) ? 0x02 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Head) ? 0x04 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Body) ? 0x08 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Hands) ? 0x10 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Legs) ? 0x20 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Feet) ? 0x40 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Ears) ? 0x80 : 0));
|
||||
data[3] = (byte)((equipFlags.HasFlag(EquipFlag.Neck) ? 0x01 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.Wrist) ? 0x02 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.RFinger) ? 0x04 : 0)
|
||||
| (equipFlags.HasFlag(EquipFlag.LFinger) ? 0x08 : 0));
|
||||
save.Customize.Load(*(Customize*) (data + 4));
|
||||
((CharacterWeapon*)(data + 30))[0] = save.MainHand;
|
||||
((CharacterWeapon*)(data + 30))[1] = save.OffHand;
|
||||
((CharacterArmor*)(data + 44))[0] = save.Head;
|
||||
((CharacterArmor*)(data + 44))[1] = save.Body;
|
||||
((CharacterArmor*)(data + 44))[2] = save.Hands;
|
||||
((CharacterArmor*)(data + 44))[3] = save.Legs;
|
||||
((CharacterArmor*)(data + 44))[4] = save.Feet;
|
||||
((CharacterArmor*)(data + 44))[5] = save.Ears;
|
||||
((CharacterArmor*)(data + 44))[6] = save.Neck;
|
||||
((CharacterArmor*)(data + 44))[7] = save.Wrists;
|
||||
((CharacterArmor*)(data + 44))[8] = save.RFinger;
|
||||
((CharacterArmor*)(data + 44))[9] = save.LFinger;
|
||||
*(float*)(data + 84) = 1f;
|
||||
data[88] = (byte)((hat ? 0x01 : 0)
|
||||
| (visor ? 0x10 : 0)
|
||||
| (weapon ? 0x02 : 0));
|
||||
|
||||
return Convert.ToBase64String(new Span<byte>(data, Base64Size));
|
||||
}
|
||||
}
|
||||
178
GlamourerOld/Designs/DesignFileSystem.cs
Normal file
178
GlamourerOld/Designs/DesignFileSystem.cs
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui.Filesystem;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public sealed class DesignFileSystem : FileSystem<Design>, IDisposable, ISavable
|
||||
{
|
||||
public static string GetDesignFileSystemFile(DalamudPluginInterface pi)
|
||||
=> Path.Combine(pi.GetPluginConfigDirectory(), "sort_order.json");
|
||||
|
||||
public readonly string DesignFileSystemFile;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly DesignManager _designManager;
|
||||
|
||||
public DesignFileSystem(DesignManager designManager, DalamudPluginInterface pi, SaveService saveService)
|
||||
{
|
||||
DesignFileSystemFile = GetDesignFileSystemFile(pi);
|
||||
_designManager = designManager;
|
||||
_saveService = saveService;
|
||||
_designManager.DesignChange += OnDataChange;
|
||||
Changed += OnChange;
|
||||
Reload();
|
||||
}
|
||||
|
||||
private void Reload()
|
||||
{
|
||||
if (Load(new FileInfo(DesignFileSystemFile), _designManager.Designs, DesignToIdentifier, DesignToName))
|
||||
_saveService.ImmediateSave(this);
|
||||
|
||||
Glamourer.Log.Debug("Reloaded design filesystem.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_designManager.DesignChange -= OnDataChange;
|
||||
}
|
||||
|
||||
public struct CreationDate : ISortMode<Design>
|
||||
{
|
||||
public string Name
|
||||
=> "Creation Date (Older First)";
|
||||
|
||||
public string Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date.";
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderBy(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
public struct InverseCreationDate : ISortMode<Design>
|
||||
{
|
||||
public string Name
|
||||
=> "Creation Date (Newer First)";
|
||||
|
||||
public string Description
|
||||
=> "In each folder, sort all subfolders lexicographically, then sort all leaves using their inverse creation date.";
|
||||
|
||||
public IEnumerable<IPath> GetChildren(Folder f)
|
||||
=> f.GetSubFolders().Cast<IPath>().Concat(f.GetLeaves().OrderByDescending(l => l.Value.CreationDate));
|
||||
}
|
||||
|
||||
private void OnChange(FileSystemChangeType type, IPath _1, IPath? _2, IPath? _3)
|
||||
{
|
||||
if (type != FileSystemChangeType.Reload)
|
||||
_saveService.QueueSave(this);
|
||||
}
|
||||
|
||||
private void OnDataChange(DesignManager.DesignChangeType type, Design design, object? data)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DesignManager.DesignChangeType.Created:
|
||||
var originalName = design.Name.Text.FixName();
|
||||
var name = originalName;
|
||||
var counter = 1;
|
||||
while (Find(name, out _))
|
||||
name = $"{originalName} ({++counter})";
|
||||
|
||||
CreateLeaf(Root, name, design);
|
||||
break;
|
||||
case DesignManager.DesignChangeType.Deleted:
|
||||
if (FindLeaf(design, out var leaf))
|
||||
Delete(leaf);
|
||||
break;
|
||||
case DesignManager.DesignChangeType.ReloadedAll:
|
||||
Reload();
|
||||
break;
|
||||
case DesignManager.DesignChangeType.Renamed when data is string oldName:
|
||||
var old = oldName.FixName();
|
||||
if (Find(old, out var child) && child is not Folder)
|
||||
Rename(child, design.Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Used for saving and loading.
|
||||
private static string DesignToIdentifier(Design design)
|
||||
=> design.Identifier.ToString();
|
||||
|
||||
private static string DesignToName(Design design)
|
||||
=> design.Name.Text.FixName();
|
||||
|
||||
private static bool DesignHasDefaultPath(Design design, string fullPath)
|
||||
{
|
||||
var regex = new Regex($@"^{Regex.Escape(DesignToName(design))}( \(\d+\))?$");
|
||||
return regex.IsMatch(fullPath);
|
||||
}
|
||||
|
||||
private static (string, bool) SaveDesign(Design design, string fullPath)
|
||||
// Only save pairs with non-default paths.
|
||||
=> DesignHasDefaultPath(design, fullPath)
|
||||
? (string.Empty, false)
|
||||
: (DesignToIdentifier(design), true);
|
||||
|
||||
// Search the entire filesystem for the leaf corresponding to a design.
|
||||
public bool FindLeaf(Design design, [NotNullWhen(true)] out Leaf? leaf)
|
||||
{
|
||||
leaf = Root.GetAllDescendants(ISortMode<Design>.Lexicographical)
|
||||
.OfType<Leaf>()
|
||||
.FirstOrDefault(l => l.Value == design);
|
||||
return leaf != null;
|
||||
}
|
||||
|
||||
internal static void MigrateOldPaths(DalamudPluginInterface pi, Dictionary<string, string> oldPaths)
|
||||
{
|
||||
if (oldPaths.Count == 0)
|
||||
return;
|
||||
|
||||
var file = GetDesignFileSystemFile(pi);
|
||||
try
|
||||
{
|
||||
JObject jObject;
|
||||
if (File.Exists(file))
|
||||
{
|
||||
var text = File.ReadAllText(file);
|
||||
jObject = JObject.Parse(text);
|
||||
var dict = jObject["Data"]?.ToObject<Dictionary<string, string>>();
|
||||
if (dict != null)
|
||||
foreach (var (key, value) in dict)
|
||||
oldPaths.TryAdd(key, value);
|
||||
|
||||
jObject["Data"] = JToken.FromObject(oldPaths);
|
||||
}
|
||||
else
|
||||
{
|
||||
jObject = new JObject
|
||||
{
|
||||
["Data"] = JToken.FromObject(oldPaths),
|
||||
["EmptyFolders"] = JToken.FromObject(Array.Empty<string>()),
|
||||
};
|
||||
}
|
||||
|
||||
var data = jObject.ToString(Formatting.Indented);
|
||||
File.WriteAllText(file, data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not migrate old folder paths to new version:\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public string ToFilename(FilenameService fileNames)
|
||||
=> fileNames.DesignFileSystem;
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{
|
||||
SaveToFile(writer, SaveDesign, true);
|
||||
}
|
||||
}
|
||||
371
GlamourerOld/Designs/DesignManager.cs
Normal file
371
GlamourerOld/Designs/DesignManager.cs
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Utility;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public class DesignManager
|
||||
{
|
||||
public const string DesignFolderName = "designs";
|
||||
public readonly string DesignFolder;
|
||||
|
||||
private readonly ItemManager _items;
|
||||
private readonly SaveService _saveService;
|
||||
private readonly List<Design> _designs = new();
|
||||
|
||||
public enum DesignChangeType
|
||||
{
|
||||
Created,
|
||||
Deleted,
|
||||
ReloadedAll,
|
||||
Renamed,
|
||||
ChangedDescription,
|
||||
AddedTag,
|
||||
RemovedTag,
|
||||
ChangedTag,
|
||||
Customize,
|
||||
Equip,
|
||||
Weapon,
|
||||
Stain,
|
||||
ApplyCustomize,
|
||||
ApplyEquip,
|
||||
Other,
|
||||
}
|
||||
|
||||
public delegate void DesignChangeDelegate(DesignChangeType type, Design design, object? changeData = null);
|
||||
|
||||
public event DesignChangeDelegate? DesignChange;
|
||||
|
||||
public IReadOnlyList<Design> Designs
|
||||
=> _designs;
|
||||
|
||||
public DesignManager(DalamudPluginInterface pi, SaveService saveService, ItemManager items)
|
||||
{
|
||||
_saveService = saveService;
|
||||
_items = items;
|
||||
DesignFolder = SetDesignFolder(pi);
|
||||
LoadDesigns();
|
||||
MigrateOldDesigns(pi, Path.Combine(new DirectoryInfo(DesignFolder).Parent!.FullName, "Designs.json"));
|
||||
}
|
||||
|
||||
private static string SetDesignFolder(DalamudPluginInterface pi)
|
||||
{
|
||||
var ret = Path.Combine(pi.GetPluginConfigDirectory(), DesignFolderName);
|
||||
if (Directory.Exists(ret))
|
||||
return ret;
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not create design folder directory at {ret}:\n{ex}");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void LoadDesigns()
|
||||
{
|
||||
_designs.Clear();
|
||||
List<(Design, string)> invalidNames = new();
|
||||
var skipped = 0;
|
||||
foreach (var file in new DirectoryInfo(DesignFolder).EnumerateFiles("*.json", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(file.FullName);
|
||||
var data = JObject.Parse(text);
|
||||
var design = Design.LoadDesign(_items, data, out var changes);
|
||||
if (design.Identifier.ToString() != Path.GetFileNameWithoutExtension(file.Name))
|
||||
invalidNames.Add((design, file.FullName));
|
||||
if (_designs.Any(f => f.Identifier == design.Identifier))
|
||||
throw new Exception($"Identifier {design.Identifier} was not unique.");
|
||||
|
||||
// TODO something when changed?
|
||||
design.Index = _designs.Count;
|
||||
_designs.Add(design);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not load design, skipped:\n{ex}");
|
||||
++skipped;
|
||||
}
|
||||
}
|
||||
|
||||
var failed = 0;
|
||||
foreach (var (design, name) in invalidNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
var correctName = _saveService.FileNames.DesignFile(design);
|
||||
File.Move(name, correctName, false);
|
||||
Glamourer.Log.Information($"Moved invalid design file from {Path.GetFileName(name)} to {Path.GetFileName(correctName)}.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
++failed;
|
||||
Glamourer.Log.Error($"Failed to move invalid design file from {Path.GetFileName(name)}:\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidNames.Count > 0)
|
||||
Glamourer.Log.Information(
|
||||
$"Moved {invalidNames.Count - failed} designs to correct names.{(failed > 0 ? $" Failed to move {failed} designs to correct names." : string.Empty)}");
|
||||
|
||||
Glamourer.Log.Information(
|
||||
$"Loaded {_designs.Count} designs.{(skipped > 0 ? $" Skipped loading {skipped} designs due to errors." : string.Empty)}");
|
||||
DesignChange?.Invoke(DesignChangeType.ReloadedAll, null!);
|
||||
}
|
||||
|
||||
public Design Create(string name)
|
||||
{
|
||||
var design = new Design(_items)
|
||||
{
|
||||
CreationDate = DateTimeOffset.UtcNow,
|
||||
Identifier = CreateNewGuid(),
|
||||
Index = _designs.Count,
|
||||
Name = name,
|
||||
};
|
||||
_designs.Add(design);
|
||||
Glamourer.Log.Debug($"Added new design {design.Identifier}.");
|
||||
_saveService.ImmediateSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Created, design);
|
||||
return design;
|
||||
}
|
||||
|
||||
public void Delete(Design design)
|
||||
{
|
||||
_designs.RemoveAt(design.Index);
|
||||
foreach (var d in _designs.Skip(design.Index + 1))
|
||||
--d.Index;
|
||||
_saveService.ImmediateDelete(design);
|
||||
}
|
||||
|
||||
public void Rename(Design design, string newName)
|
||||
{
|
||||
var oldName = design.Name.Text;
|
||||
_saveService.ImmediateDelete(design);
|
||||
design.Name = newName;
|
||||
Glamourer.Log.Debug($"Renamed design {design.Identifier}.");
|
||||
_saveService.ImmediateSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Renamed, design, oldName);
|
||||
}
|
||||
|
||||
public void ChangeDescription(Design design, string description)
|
||||
{
|
||||
design.Description = description;
|
||||
Glamourer.Log.Debug($"Changed description of design {design.Identifier}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ChangedDescription, design);
|
||||
}
|
||||
|
||||
public void AddTag(Design design, string tag)
|
||||
{
|
||||
if (design.Tags.Contains(tag))
|
||||
return;
|
||||
|
||||
design.Tags = design.Tags.Append(tag).OrderBy(t => t).ToArray();
|
||||
var idx = design.Tags.IndexOf(tag);
|
||||
Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.AddedTag, design);
|
||||
}
|
||||
|
||||
public void RemoveTag(Design design, string tag)
|
||||
{
|
||||
var idx = design.Tags.IndexOf(tag);
|
||||
if (idx >= 0)
|
||||
RemoveTag(design, idx);
|
||||
}
|
||||
|
||||
public void RemoveTag(Design design, int tagIdx)
|
||||
{
|
||||
var oldTag = design.Tags[tagIdx];
|
||||
design.Tags = design.Tags.Take(tagIdx).Concat(design.Tags.Skip(tagIdx + 1)).ToArray();
|
||||
Glamourer.Log.Debug($"Removed tag {oldTag} at {tagIdx} from design {design.Identifier}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.RemovedTag, design);
|
||||
}
|
||||
|
||||
|
||||
public void RenameTag(Design design, int tagIdx, string newTag)
|
||||
{
|
||||
var oldTag = design.Tags[tagIdx];
|
||||
if (oldTag == newTag)
|
||||
return;
|
||||
|
||||
design.Tags[tagIdx] = newTag;
|
||||
Array.Sort(design.Tags);
|
||||
Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ChangedTag, design);
|
||||
}
|
||||
|
||||
public void ChangeCustomize(Design design, CustomizeIndex idx, CustomizeValue value)
|
||||
{
|
||||
var old = design.GetCustomize(idx);
|
||||
if (!design.SetCustomize(idx, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Changed customize {idx} in design {design.Identifier} from {old.Value} to {value.Value}");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Customize, design, idx);
|
||||
}
|
||||
|
||||
public void ChangeApplyCustomize(Design design, CustomizeIndex idx, bool value)
|
||||
{
|
||||
if (!design.SetApplyCustomize(idx, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set applying of customization {idx} to {value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ApplyCustomize, design, idx);
|
||||
}
|
||||
|
||||
public void ChangeEquip(Design design, EquipSlot slot, uint itemId, Lumina.Excel.GeneratedSheets.Item? item = null)
|
||||
{
|
||||
var old = design.Armor(slot);
|
||||
if (!design.SetArmor(_items, slot, itemId, item))
|
||||
return;
|
||||
|
||||
var n = design.Armor(slot);
|
||||
Glamourer.Log.Debug(
|
||||
$"Set {slot} equipment piece in design {design.Identifier} from {old.Name} ({old.ItemId}) to {n.Name} ({n.ItemId}).");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Equip, design, slot);
|
||||
}
|
||||
|
||||
public void ChangeWeapon(Design design, uint itemId, EquipSlot offhand, Lumina.Excel.GeneratedSheets.Item? item = null)
|
||||
{
|
||||
var (old, change, n) = offhand == EquipSlot.OffHand
|
||||
? (design.WeaponOff, design.SetOffhand(_items, itemId, item), design.WeaponOff)
|
||||
: (design.WeaponMain, design.SetMainhand(_items, itemId, item), design.WeaponMain);
|
||||
if (!change)
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug(
|
||||
$"Set {offhand} weapon in design {design.Identifier} from {old.Name} ({old.ItemId}) to {n.Name} ({n.ItemId}).");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Weapon, design, offhand);
|
||||
}
|
||||
|
||||
public void ChangeApplyEquip(Design design, EquipSlot slot, bool value)
|
||||
{
|
||||
if (!design.SetApplyEquip(slot, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set applying of {slot} equipment piece to {value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.ApplyEquip, design, slot);
|
||||
}
|
||||
|
||||
public void ChangeStain(Design design, EquipSlot slot, StainId stain)
|
||||
{
|
||||
if (!design.SetStain(slot, stain))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stain.Value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Stain, design, slot);
|
||||
}
|
||||
|
||||
public void ChangeApplyStain(Design design, EquipSlot slot, bool value)
|
||||
{
|
||||
if (!design.SetApplyStain(slot, value))
|
||||
return;
|
||||
|
||||
Glamourer.Log.Debug($"Set applying of stain of {slot} equipment piece to {value}.");
|
||||
_saveService.QueueSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Stain, design, slot);
|
||||
}
|
||||
|
||||
private Guid CreateNewGuid()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
if (_designs.All(d => d.Identifier != guid))
|
||||
return guid;
|
||||
}
|
||||
}
|
||||
|
||||
private bool Add(Design design, string? message)
|
||||
{
|
||||
if (_designs.Any(d => d == design || d.Identifier == design.Identifier))
|
||||
return false;
|
||||
|
||||
design.Index = _designs.Count;
|
||||
_designs.Add(design);
|
||||
if (!message.IsNullOrEmpty())
|
||||
Glamourer.Log.Debug(message);
|
||||
_saveService.ImmediateSave(design);
|
||||
DesignChange?.Invoke(DesignChangeType.Created, design);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void MigrateOldDesigns(DalamudPluginInterface pi, string filePath)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
return;
|
||||
|
||||
var errors = 0;
|
||||
var successes = 0;
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(filePath);
|
||||
var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(text) ?? new Dictionary<string, string>();
|
||||
var migratedFileSystemPaths = new Dictionary<string, string>(dict.Count);
|
||||
foreach (var (name, base64) in dict)
|
||||
{
|
||||
try
|
||||
{
|
||||
var actualName = Path.GetFileName(name);
|
||||
var design = new Design(_items)
|
||||
{
|
||||
CreationDate = DateTimeOffset.UtcNow,
|
||||
Identifier = CreateNewGuid(),
|
||||
Name = actualName,
|
||||
};
|
||||
design.MigrateBase64(_items, base64);
|
||||
Add(design, $"Migrated old design to {design.Identifier}.");
|
||||
migratedFileSystemPaths.Add(design.Identifier.ToString(), name);
|
||||
++successes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not migrate design {name}:\n{ex}");
|
||||
++errors;
|
||||
}
|
||||
}
|
||||
|
||||
DesignFileSystem.MigrateOldPaths(pi, migratedFileSystemPaths);
|
||||
Glamourer.Log.Information($"Successfully migrated {successes} old designs. Failed to migrate {errors} designs.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not migrate old design file {filePath}:\n{e}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Move(filePath, Path.ChangeExtension(filePath, ".json.bak"));
|
||||
Glamourer.Log.Information($"Moved migrated design file {filePath} to backup file.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Glamourer.Log.Error($"Could not move migrated design file {filePath} to backup file:\n{ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
74
GlamourerOld/Designs/EquipFlag.cs
Normal file
74
GlamourerOld/Designs/EquipFlag.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
[Flags]
|
||||
public enum EquipFlag : uint
|
||||
{
|
||||
Head = 0x00000001,
|
||||
Body = 0x00000002,
|
||||
Hands = 0x00000004,
|
||||
Legs = 0x00000008,
|
||||
Feet = 0x00000010,
|
||||
Ears = 0x00000020,
|
||||
Neck = 0x00000040,
|
||||
Wrist = 0x00000080,
|
||||
RFinger = 0x00000100,
|
||||
LFinger = 0x00000200,
|
||||
Mainhand = 0x00000400,
|
||||
Offhand = 0x00000800,
|
||||
HeadStain = 0x00001000,
|
||||
BodyStain = 0x00002000,
|
||||
HandsStain = 0x00004000,
|
||||
LegsStain = 0x00008000,
|
||||
FeetStain = 0x00010000,
|
||||
EarsStain = 0x00020000,
|
||||
NeckStain = 0x00040000,
|
||||
WristStain = 0x00080000,
|
||||
RFingerStain = 0x00100000,
|
||||
LFingerStain = 0x00200000,
|
||||
MainhandStain = 0x00400000,
|
||||
OffhandStain = 0x00800000,
|
||||
}
|
||||
|
||||
public static class EquipFlagExtensions
|
||||
{
|
||||
public const EquipFlag All = (EquipFlag)(((uint)EquipFlag.OffhandStain << 1) - 1);
|
||||
|
||||
public static EquipFlag ToFlag(this EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => EquipFlag.Mainhand,
|
||||
EquipSlot.OffHand => EquipFlag.Offhand,
|
||||
EquipSlot.Head => EquipFlag.Head,
|
||||
EquipSlot.Body => EquipFlag.Body,
|
||||
EquipSlot.Hands => EquipFlag.Hands,
|
||||
EquipSlot.Legs => EquipFlag.Legs,
|
||||
EquipSlot.Feet => EquipFlag.Feet,
|
||||
EquipSlot.Ears => EquipFlag.Ears,
|
||||
EquipSlot.Neck => EquipFlag.Neck,
|
||||
EquipSlot.Wrists => EquipFlag.Wrist,
|
||||
EquipSlot.RFinger => EquipFlag.RFinger,
|
||||
EquipSlot.LFinger => EquipFlag.LFinger,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public static EquipFlag ToStainFlag(this EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => EquipFlag.MainhandStain,
|
||||
EquipSlot.OffHand => EquipFlag.OffhandStain,
|
||||
EquipSlot.Head => EquipFlag.HeadStain,
|
||||
EquipSlot.Body => EquipFlag.BodyStain,
|
||||
EquipSlot.Hands => EquipFlag.HandsStain,
|
||||
EquipSlot.Legs => EquipFlag.LegsStain,
|
||||
EquipSlot.Feet => EquipFlag.FeetStain,
|
||||
EquipSlot.Ears => EquipFlag.EarsStain,
|
||||
EquipSlot.Neck => EquipFlag.NeckStain,
|
||||
EquipSlot.Wrists => EquipFlag.WristStain,
|
||||
EquipSlot.RFinger => EquipFlag.RFingerStain,
|
||||
EquipSlot.LFinger => EquipFlag.LFingerStain,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
496
GlamourerOld/Designs/ModelData.cs
Normal file
496
GlamourerOld/Designs/ModelData.cs
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.String.Functions;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public struct ItemPiece
|
||||
{
|
||||
public string Name;
|
||||
public uint ItemId;
|
||||
public SetId ModelId;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ModelFlags : ushort
|
||||
{
|
||||
HatVisible = 0x01,
|
||||
WeaponVisible = 0x02,
|
||||
VisorToggled = 0x04,
|
||||
}
|
||||
|
||||
public struct ModelData
|
||||
{
|
||||
public readonly unsafe ModelData Clone()
|
||||
{
|
||||
var data = new ModelData(MainHand);
|
||||
fixed (void* ptr = &this)
|
||||
{
|
||||
MemoryUtility.MemCpyUnchecked(&data, ptr, sizeof(ModelData));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public Customize Customize = Customize.Default;
|
||||
public ModelFlags Flags = ModelFlags.HatVisible | ModelFlags.WeaponVisible;
|
||||
public CharacterWeapon MainHand;
|
||||
public CharacterWeapon OffHand = CharacterWeapon.Empty;
|
||||
|
||||
public uint ModelId = 0;
|
||||
public CharacterArmor Head = CharacterArmor.Empty;
|
||||
public CharacterArmor Body = CharacterArmor.Empty;
|
||||
public CharacterArmor Hands = CharacterArmor.Empty;
|
||||
public CharacterArmor Legs = CharacterArmor.Empty;
|
||||
public CharacterArmor Feet = CharacterArmor.Empty;
|
||||
public CharacterArmor Ears = CharacterArmor.Empty;
|
||||
public CharacterArmor Neck = CharacterArmor.Empty;
|
||||
public CharacterArmor Wrists = CharacterArmor.Empty;
|
||||
public CharacterArmor RFinger = CharacterArmor.Empty;
|
||||
public CharacterArmor LFinger = CharacterArmor.Empty;
|
||||
|
||||
public ModelData(CharacterWeapon mainHand)
|
||||
=> MainHand = mainHand;
|
||||
|
||||
public readonly CharacterArmor Armor(EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => MainHand.ToArmor(),
|
||||
EquipSlot.OffHand => OffHand.ToArmor(),
|
||||
EquipSlot.Head => Head,
|
||||
EquipSlot.Body => Body,
|
||||
EquipSlot.Hands => Hands,
|
||||
EquipSlot.Legs => Legs,
|
||||
EquipSlot.Feet => Feet,
|
||||
EquipSlot.Ears => Ears,
|
||||
EquipSlot.Neck => Neck,
|
||||
EquipSlot.Wrists => Wrists,
|
||||
EquipSlot.RFinger => RFinger,
|
||||
EquipSlot.LFinger => LFinger,
|
||||
_ => CharacterArmor.Empty,
|
||||
};
|
||||
|
||||
public readonly CharacterWeapon Piece(EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => MainHand,
|
||||
EquipSlot.OffHand => OffHand,
|
||||
EquipSlot.Head => Head.ToWeapon(),
|
||||
EquipSlot.Body => Body.ToWeapon(),
|
||||
EquipSlot.Hands => Hands.ToWeapon(),
|
||||
EquipSlot.Legs => Legs.ToWeapon(),
|
||||
EquipSlot.Feet => Feet.ToWeapon(),
|
||||
EquipSlot.Ears => Ears.ToWeapon(),
|
||||
EquipSlot.Neck => Neck.ToWeapon(),
|
||||
EquipSlot.Wrists => Wrists.ToWeapon(),
|
||||
EquipSlot.RFinger => RFinger.ToWeapon(),
|
||||
EquipSlot.LFinger => LFinger.ToWeapon(),
|
||||
_ => CharacterWeapon.Empty,
|
||||
};
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, byte variant, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, WeaponType type, byte variant, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Type, type);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Type, type);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, byte variant, StainId stain, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref MainHand.Stain, stain);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref OffHand.Stain, stain);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Head.Stain, stain);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Body.Stain, stain);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Hands.Stain, stain);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Legs.Stain, stain);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Feet.Stain, stain);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Ears.Stain, stain);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Neck.Stain, stain);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Wrists.Stain, stain);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref RFinger.Stain, stain);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref LFinger.Stain, stain);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetPiece(EquipSlot slot, CharacterWeapon weapon, out CharacterWeapon ret)
|
||||
=> SetPiece(slot, weapon.Set, weapon.Type, (byte)weapon.Variant, weapon.Stain, out ret);
|
||||
|
||||
public bool SetPiece(EquipSlot slot, CharacterArmor armor, out CharacterWeapon ret)
|
||||
=> SetPiece(slot, armor.Set, armor.Variant, armor.Stain, out ret);
|
||||
|
||||
public bool SetPiece(EquipSlot slot, SetId model, WeaponType type, byte variant, StainId stain, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes |= SetIfDifferent(ref MainHand.Set, model);
|
||||
changes |= SetIfDifferent(ref MainHand.Type, type);
|
||||
changes |= SetIfDifferent(ref MainHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref MainHand.Stain, stain);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes |= SetIfDifferent(ref OffHand.Set, model);
|
||||
changes |= SetIfDifferent(ref OffHand.Type, type);
|
||||
changes |= SetIfDifferent(ref OffHand.Variant, variant);
|
||||
changes |= SetIfDifferent(ref OffHand.Stain, stain);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes |= SetIfDifferent(ref Head.Set, model);
|
||||
changes |= SetIfDifferent(ref Head.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Head.Stain, stain);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes |= SetIfDifferent(ref Body.Set, model);
|
||||
changes |= SetIfDifferent(ref Body.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Body.Stain, stain);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes |= SetIfDifferent(ref Hands.Set, model);
|
||||
changes |= SetIfDifferent(ref Hands.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Hands.Stain, stain);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes |= SetIfDifferent(ref Legs.Set, model);
|
||||
changes |= SetIfDifferent(ref Legs.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Legs.Stain, stain);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes |= SetIfDifferent(ref Feet.Set, model);
|
||||
changes |= SetIfDifferent(ref Feet.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Feet.Stain, stain);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes |= SetIfDifferent(ref Ears.Set, model);
|
||||
changes |= SetIfDifferent(ref Ears.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Ears.Stain, stain);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes |= SetIfDifferent(ref Neck.Set, model);
|
||||
changes |= SetIfDifferent(ref Neck.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Neck.Stain, stain);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes |= SetIfDifferent(ref Wrists.Set, model);
|
||||
changes |= SetIfDifferent(ref Wrists.Variant, variant);
|
||||
changes |= SetIfDifferent(ref Wrists.Stain, stain);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes |= SetIfDifferent(ref RFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref RFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref RFinger.Stain, stain);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes |= SetIfDifferent(ref LFinger.Set, model);
|
||||
changes |= SetIfDifferent(ref LFinger.Variant, variant);
|
||||
changes |= SetIfDifferent(ref LFinger.Stain, stain);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
public StainId Stain(EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => MainHand.Stain,
|
||||
EquipSlot.OffHand => OffHand.Stain,
|
||||
EquipSlot.Head => Head.Stain,
|
||||
EquipSlot.Body => Body.Stain,
|
||||
EquipSlot.Hands => Hands.Stain,
|
||||
EquipSlot.Legs => Legs.Stain,
|
||||
EquipSlot.Feet => Feet.Stain,
|
||||
EquipSlot.Ears => Ears.Stain,
|
||||
EquipSlot.Neck => Neck.Stain,
|
||||
EquipSlot.Wrists => Wrists.Stain,
|
||||
EquipSlot.RFinger => RFinger.Stain,
|
||||
EquipSlot.LFinger => LFinger.Stain,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public bool SetStain(EquipSlot slot, StainId stain, out CharacterWeapon ret)
|
||||
{
|
||||
var changes = false;
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand:
|
||||
changes = SetIfDifferent(ref MainHand.Stain, stain);
|
||||
ret = MainHand;
|
||||
return changes;
|
||||
case EquipSlot.OffHand:
|
||||
changes = SetIfDifferent(ref OffHand.Stain, stain);
|
||||
ret = OffHand;
|
||||
return changes;
|
||||
case EquipSlot.Head:
|
||||
changes = SetIfDifferent(ref Head.Stain, stain);
|
||||
ret = Head.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Body:
|
||||
changes = SetIfDifferent(ref Body.Stain, stain);
|
||||
ret = Body.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Hands:
|
||||
changes = SetIfDifferent(ref Hands.Stain, stain);
|
||||
ret = Hands.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Legs:
|
||||
changes = SetIfDifferent(ref Legs.Stain, stain);
|
||||
ret = Legs.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Feet:
|
||||
changes = SetIfDifferent(ref Feet.Stain, stain);
|
||||
ret = Feet.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Ears:
|
||||
changes = SetIfDifferent(ref Ears.Stain, stain);
|
||||
ret = Ears.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Neck:
|
||||
changes = SetIfDifferent(ref Neck.Stain, stain);
|
||||
ret = Neck.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.Wrists:
|
||||
changes = SetIfDifferent(ref Wrists.Stain, stain);
|
||||
ret = Wrists.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.RFinger:
|
||||
changes = SetIfDifferent(ref RFinger.Stain, stain);
|
||||
ret = RFinger.ToWeapon();
|
||||
return changes;
|
||||
case EquipSlot.LFinger:
|
||||
changes = SetIfDifferent(ref LFinger.Stain, stain);
|
||||
ret = LFinger.ToWeapon();
|
||||
return changes;
|
||||
default:
|
||||
ret = CharacterWeapon.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SetIfDifferent<T>(ref T old, T value) where T : IEquatable<T>
|
||||
{
|
||||
if (old.Equals(value))
|
||||
return false;
|
||||
|
||||
old = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
105
GlamourerOld/Designs/Structs.cs
Normal file
105
GlamourerOld/Designs/Structs.cs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
using Dalamud.Utility;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Designs;
|
||||
|
||||
public readonly struct Item
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly uint ItemId;
|
||||
public readonly CharacterArmor Model;
|
||||
|
||||
public SetId ModelBase
|
||||
=> Model.Set;
|
||||
|
||||
public byte Variant
|
||||
=> Model.Variant;
|
||||
|
||||
public StainId Stain
|
||||
=> Model.Stain;
|
||||
|
||||
public Item WithStain(StainId id)
|
||||
=> new(Name, ItemId, Model with { Stain = id });
|
||||
|
||||
public Item(string name, uint itemId, CharacterArmor armor)
|
||||
{
|
||||
Name = name;
|
||||
ItemId = itemId;
|
||||
Model.Set = armor.Set;
|
||||
Model.Variant = armor.Variant;
|
||||
Model.Stain = armor.Stain;
|
||||
}
|
||||
|
||||
public Item(Lumina.Excel.GeneratedSheets.Item item)
|
||||
{
|
||||
Name = string.Intern(item.Name.ToDalamudString().TextValue);
|
||||
ItemId = item.RowId;
|
||||
Model.Set = (SetId)item.ModelMain;
|
||||
Model.Variant = (byte)(item.ModelMain >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Weapon
|
||||
{
|
||||
public readonly string Name = string.Empty;
|
||||
public readonly uint ItemId;
|
||||
public readonly FullEquipType Type;
|
||||
public readonly bool Valid;
|
||||
public readonly CharacterWeapon Model;
|
||||
|
||||
public SetId ModelBase
|
||||
=> Model.Set;
|
||||
|
||||
public WeaponType WeaponBase
|
||||
=> Model.Type;
|
||||
|
||||
public byte Variant
|
||||
=> (byte)Model.Variant;
|
||||
|
||||
public StainId Stain
|
||||
=> Model.Stain;
|
||||
|
||||
|
||||
public Weapon WithStain(StainId id)
|
||||
=> new(Name, ItemId, Model with { Stain = id }, Type);
|
||||
|
||||
public Weapon(string name, uint itemId, CharacterWeapon weapon, FullEquipType type)
|
||||
{
|
||||
Name = name;
|
||||
ItemId = itemId;
|
||||
Type = type;
|
||||
Valid = true;
|
||||
Model.Set = weapon.Set;
|
||||
Model.Type = weapon.Type;
|
||||
Model.Variant = (byte)weapon.Variant;
|
||||
Model.Stain = weapon.Stain;
|
||||
}
|
||||
|
||||
public static Weapon Offhand(string name, uint itemId, CharacterWeapon weapon, FullEquipType type)
|
||||
{
|
||||
var offType = type.Offhand();
|
||||
return offType is FullEquipType.Unknown
|
||||
? new Weapon()
|
||||
: new Weapon(name, itemId, weapon, offType);
|
||||
}
|
||||
|
||||
public Weapon(Lumina.Excel.GeneratedSheets.Item item, bool offhand)
|
||||
{
|
||||
Name = string.Intern(item.Name.ToDalamudString().TextValue);
|
||||
ItemId = item.RowId;
|
||||
Type = item.ToEquipType();
|
||||
var offType = Type.Offhand();
|
||||
var model = offhand && offType == Type ? item.ModelSub : item.ModelMain;
|
||||
Valid = Type.ToSlot() switch
|
||||
{
|
||||
EquipSlot.MainHand when !offhand => true,
|
||||
EquipSlot.MainHand when offhand && offType == Type => true,
|
||||
EquipSlot.OffHand when offhand => true,
|
||||
_ => false,
|
||||
};
|
||||
Model.Set = (SetId)model;
|
||||
Model.Type = (WeaponType)(model >> 16);
|
||||
Model.Variant = (byte)(model >> 32);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue