This commit is contained in:
Ottermandias 2023-06-19 23:59:22 +02:00
parent 80ab57e96d
commit d1d369a56b
31 changed files with 1637 additions and 80 deletions

View file

@ -1,8 +1,7 @@
using System;
using System.Linq;
using System.Security.AccessControl;
using System.Runtime.CompilerServices;
using Dalamud.Data;
using Dalamud.Interface.Internal.Notifications;
using Dalamud.Plugin;
using Glamourer.Customization;
using Penumbra.GameData.Enums;
@ -60,6 +59,26 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
};
}
/// <summary> Returns whether a clan is valid. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsClanValid(SubRace clan)
=> AwaitedService.Clans.Contains(clan);
/// <summary> Returns whether a gender is valid for the given race. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsGenderValid(Race race, Gender gender)
=> race is Race.Hrothgar ? gender == Gender.Male : AwaitedService.Genders.Contains(gender);
/// <summary> Returns whether a customization value is valid for a given clan/gender set and face. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool IsCustomizationValid(CustomizationSet set, CustomizeValue face, CustomizeIndex type, CustomizeValue value)
=> set.DataByValue(type, value, out _, face) >= 0;
/// <summary> Returns whether a customization value is valid for a given clan, gender and face. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsCustomizationValid(SubRace race, Gender gender, CustomizeValue face, CustomizeIndex type, CustomizeValue value)
=> AwaitedService.GetList(race, gender).DataByValue(type, value, out _, face) >= 0;
/// <summary>
/// Check that the given race and clan are valid.
/// The returned race and clan fit together and are valid.
@ -67,7 +86,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
/// </summary>
public string ValidateClan(SubRace clan, Race race, out Race actualRace, out SubRace actualClan)
{
if (AwaitedService.Clans.Contains(clan))
if (IsClanValid(clan))
{
actualClan = clan;
actualRace = actualClan.ToRace();
@ -113,7 +132,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
}
// TODO: Female Hrothgar
if (gender == Gender.Female && race == Race.Hrothgar)
if (gender is Gender.Female && race is Race.Hrothgar)
{
actualGender = Gender.Male;
return $"{Race.Hrothgar.ToName()} do not currently support {Gender.Female.ToName()} characters, reset to {Gender.Male.ToName()}.";
@ -134,7 +153,6 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
return modelId != 0 ? $"Model IDs different from 0 are not currently allowed, reset {modelId} to 0." : string.Empty;
}
/// <summary>
/// Validate a single customization value against a given set of race and gender (and face).
/// The returned actualValue is either the correct value or the one with index 0.
@ -143,9 +161,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
public static string ValidateCustomizeValue(CustomizationSet set, CustomizeValue face, CustomizeIndex index, CustomizeValue value,
out CustomizeValue actualValue)
{
var count = set.Count(index, face);
var idx = set.DataByValue(index, value, out var data, face);
if (idx >= 0 && idx < count)
if (IsCustomizationValid(set, face, index, value))
{
actualValue = value;
return string.Empty;

View file

@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using Dalamud.Data;
using Dalamud.Plugin;
using Lumina.Excel;
@ -135,6 +136,14 @@ public class ItemManager : IDisposable
: new EquipItem($"Unknown ({id.Value}-{type.Value}-{variant})", 0, 0, id, type, variant, 0);
}
/// <summary> Returns whether an item id represents a valid item for a slot and gives the item. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsItemValid(EquipSlot slot, uint itemId, out EquipItem item)
{
item = Resolve(slot, itemId);
return item.Valid;
}
/// <summary>
/// Check whether an item id resolves to an existing item of the correct slot (which should not be weapons.)
/// The returned item is either the resolved correct item, or the Nothing item for that slot.
@ -145,22 +154,26 @@ public class ItemManager : IDisposable
if (slot is EquipSlot.MainHand or EquipSlot.OffHand)
throw new Exception("Internal Error: Used armor functionality for weapons.");
item = Resolve(slot, itemId);
if (item.Valid)
if (IsItemValid(slot, itemId, out item))
return string.Empty;
item = NothingItem(slot);
return $"The {slot.ToName()} item {itemId} does not exist, reset to Nothing.";
}
/// <summary> Returns whether a stain id is a valid stain. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsStainValid(StainId stain)
=> stain.Value == 0 || Stains.ContainsKey(stain);
/// <summary>
/// Check whether a stain id is an existing stain.
/// The returned stain id is either the input or 0.
/// The return value is an empty string if there was no problem and a warning otherwise.
/// </summary>
public string ValidateStain(StainId stain, out StainId ret)
public string ValidateStain(StainId stain, out StainId ret)
{
if (stain.Value == 0 || Stains.ContainsKey(stain))
if (IsStainValid(stain))
{
ret = stain;
return string.Empty;
@ -170,6 +183,19 @@ public class ItemManager : IDisposable
return $"The Stain {stain} does not exist, reset to unstained.";
}
/// <summary> Returns whether an offhand is valid given the required offhand type. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsOffhandValid(FullEquipType offType, uint offId, out EquipItem off)
{
off = Resolve(offType, offId);
return off.Valid;
}
/// <summary> Returns whether an offhand is valid given mainhand. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsOffhandValid(in EquipItem main, uint offId, out EquipItem off)
=> IsOffhandValid(main.Type.Offhand(), offId, out off);
/// <summary>
/// Check whether a combination of an item id for a mainhand and for an offhand is valid.
/// The returned items are either the resolved correct items,
@ -180,42 +206,30 @@ public class ItemManager : IDisposable
public string ValidateWeapons(uint mainId, uint offId, out EquipItem main, out EquipItem off)
{
var ret = string.Empty;
main = Resolve(EquipSlot.MainHand, mainId);
if (!main.Valid)
if (!IsItemValid(EquipSlot.MainHand, mainId, out main))
{
main = DefaultSword;
ret = $"The mainhand weapon {mainId} does not exist, reset to default sword.";
ret = $"The mainhand weapon {mainId} does not exist, reset to default sword.";
}
var offhandType = main.Type.Offhand();
off = Resolve(offhandType, offId);
if (off.Valid)
var offType = main.Type.Offhand();
if (IsOffhandValid(offType, offId, out off))
return ret;
// Try implicit offhand.
off = Resolve(offhandType, mainId);
if (off.Valid)
// Can not be set to default sword before because then it could not be valid.
if (IsOffhandValid(offType, mainId, out off))
return $"The offhand weapon {offId} does not exist, reset to implied offhand.";
if (FullEquipTypeExtensions.OffhandTypes.Contains(offType))
{
// Can not be set to default sword before because then it could not be valid.
ret = $"The offhand weapon {offId} does not exist, reset to implied offhand.";
}
else
{
if (FullEquipTypeExtensions.OffhandTypes.Contains(offhandType))
{
main = DefaultSword;
off = NothingItem(FullEquipType.Shield);
ret =
$"The offhand weapon {offId} does not exist, but no default could be restored, reset mainhand to default sword and offhand to nothing.";
}
else
{
off = NothingItem(offhandType);
if (ret.Length == 0)
ret = $"The offhand weapon {offId} does not exist, reset to no offhand.";
}
main = DefaultSword;
off = NothingItem(FullEquipType.Shield);
return
$"The offhand weapon {offId} does not exist, but no default could be restored, reset mainhand to default sword and offhand to nothing.";
}
return ret;
off = NothingItem(offType);
return ret.Length == 0 ? $"The offhand weapon {offId} does not exist, reset to no offhand." : ret;
}
}

View file

@ -2,7 +2,10 @@
using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Gui;
using Glamourer.Gui.Customization;
using Glamourer.Gui.Tabs;
using Glamourer.Gui.Tabs.ActorTab;
using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.State;
@ -49,7 +52,8 @@ public static class ServiceManager
private static IServiceCollection AddEvents(this IServiceCollection services)
=> services.AddSingleton<VisorStateChanged>()
.AddSingleton<UpdatedSlot>()
.AddSingleton<DesignChanged>();
.AddSingleton<DesignChanged>()
.AddSingleton<StateChanged>();
private static IServiceCollection AddData(this IServiceCollection services)
=> services.AddSingleton<IdentifierService>()
@ -76,8 +80,15 @@ public static class ServiceManager
private static IServiceCollection AddUi(this IServiceCollection services)
=> services.AddSingleton<DebugTab>()
.AddSingleton<SettingsTab>()
.AddSingleton<ActorTab>()
.AddSingleton<ActorSelector>()
.AddSingleton<ActorPanel>()
.AddSingleton<MainWindow>()
.AddSingleton<GlamourerWindowSystem>();
.AddSingleton<GlamourerWindowSystem>()
.AddSingleton<CustomizationDrawer>()
.AddSingleton<DesignFileSystemSelector>()
.AddSingleton<DesignPanel>()
.AddSingleton<DesignTab>();
private static IServiceCollection AddApi(this IServiceCollection services)
=> services.AddSingleton<CommandService>();