diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index da9873d8d..5101657ba 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -158,7 +158,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState ConditionFlag.NormalConditions, ConditionFlag.Jumping, ConditionFlag.Mounted, - ConditionFlag.UsingParasol]); + ConditionFlag.UsingFashionAccessory]); blockingFlag = blockingConditions.FirstOrDefault(); return blockingFlag == 0; diff --git a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs index 7003893ff..b9633d6e3 100644 --- a/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs +++ b/Dalamud/Game/Text/Evaluator/SeStringEvaluator.cs @@ -939,9 +939,7 @@ internal class SeStringEvaluator : IServiceType, ISeStringEvaluator if (p.Type == ReadOnlySePayloadType.Text) { - context.Builder.Append( - context.CultureInfo.TextInfo.ToTitleCase(Encoding.UTF8.GetString(p.Body.Span))); - + context.Builder.Append(Encoding.UTF8.GetString(p.Body.Span).ToUpper(true, true, false, context.Language)); continue; } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs index bc0bd0ac9..3cb5d3242 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/NounProcessorWidget.cs @@ -9,6 +9,7 @@ using Dalamud.Game.Text.Noun.Enums; using Dalamud.Interface.Utility.Raii; using ImGuiNET; + using Lumina.Data; using Lumina.Excel; using Lumina.Excel.Sheets; @@ -21,7 +22,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; internal class NounProcessorWidget : IDataWindowWidget { /// A list of German grammatical cases. - internal static readonly string[] GermanCases = ["Nominative", "Genitive", "Dative", "Accusative"]; + internal static readonly string[] GermanCases = [string.Empty, "Nominative", "Genitive", "Dative", "Accusative"]; private static readonly Type[] NounSheets = [ typeof(Aetheryte), @@ -156,7 +157,7 @@ internal class NounProcessorWidget : IDataWindowWidget GrammaticalCase = grammaticalCase, }; var output = nounProcessor.ProcessNoun(nounParams).ExtractText().Replace("\"", "\\\""); - var caseParam = language == ClientLanguage.German ? $"(int)GermanCases.{GermanCases[grammaticalCase]}" : "1"; + var caseParam = language == ClientLanguage.German ? $"(int)GermanCases.{GermanCases[grammaticalCase + 1]}" : "1"; sb.AppendLine($"new(nameof(LSheets.{sheetType.Name}), {this.rowId}, ClientLanguage.{language}, {this.amount}, (int){articleTypeEnumType.Name}.{Enum.GetName(articleTypeEnumType, articleType)}, {caseParam}, \"{output}\"),"); } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs index 92e57ddac..2175b8be1 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeStringCreatorWidget.cs @@ -1014,7 +1014,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget ImGui.TextUnformatted(Enum.GetName(articleTypeEnumType, u32)); } - if (macroCode is MacroCode.DeNoun && exprIdx == 4 && u32 is >= 0 and <= 3) + if (macroCode is MacroCode.DeNoun && exprIdx == 4 && u32 is >= 0 and <= 4) { ImGui.SameLine(); ImGui.TextUnformatted(NounProcessorWidget.GermanCases[u32]); diff --git a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs index ce135b947..d40184a76 100644 --- a/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs +++ b/Dalamud/Plugin/Internal/AutoUpdate/AutoUpdateManager.cs @@ -501,7 +501,7 @@ internal class AutoUpdateManager : IServiceType condition.OnlyAny(ConditionFlag.NormalConditions, ConditionFlag.Jumping, ConditionFlag.Mounted, - ConditionFlag.UsingParasol); + ConditionFlag.UsingFashionAccessory); } private bool IsPluginManagerReady() diff --git a/Dalamud/Utility/StringExtensions.cs b/Dalamud/Utility/StringExtensions.cs index 50973e338..c28aebab2 100644 --- a/Dalamud/Utility/StringExtensions.cs +++ b/Dalamud/Utility/StringExtensions.cs @@ -1,5 +1,8 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Text; + +using Dalamud.Game; using FFXIVClientStructs.FFXIV.Client.UI; @@ -10,6 +13,9 @@ namespace Dalamud.Utility; /// public static class StringExtensions { + private static readonly string[] CommonExcludedWords = ["sas", "zos", "van", "nan", "tol", "deus", "mal", "de", "rem", "out", "yae", "bas", "cen", "quo", "viator", "la"]; + private static readonly string[] EnglishExcludedWords = ["of", "the", "to", "and", "a", "an", "or", "at", "by", "for", "in", "on", "with", "from", .. CommonExcludedWords]; + /// /// An extension method to chain usage of string.Format. /// @@ -77,7 +83,7 @@ public static class StringExtensions public static string StripSoftHyphen(this string input) => input.Replace("\u00AD", string.Empty); /// - /// Truncates the given string to the specified maximum number of characters, + /// Truncates the given string to the specified maximum number of characters, /// appending an ellipsis if truncation occurs. /// /// The string to truncate. @@ -88,4 +94,128 @@ public static class StringExtensions { return string.IsNullOrEmpty(input) || input.Length <= maxChars ? input : input[..maxChars] + ellipses; } + + /// + /// Converts the input string to uppercase based on specified options like capitalizing the first character, + /// normalizing vowels, and excluding certain words based on the selected language. + /// + /// The input string to be converted to uppercase. + /// Whether to capitalize only the first character of the string. + /// Whether to capitalize the first letter of each word. + /// Whether to normalize vowels to uppercase if they appear at the beginning of a word. + /// The language context used to determine which words to exclude from capitalization. + /// A new string with the appropriate characters converted to uppercase. + /// This is a C# implementation of Client::System::String::Utf8String.ToUpper with word exclusion lists as used by the HeadAll macro. + public static string ToUpper(this string input, bool firstCharOnly, bool everyWord, bool normalizeVowels, ClientLanguage language) + { + return ToUpper(input, firstCharOnly, everyWord, normalizeVowels, language switch + { + ClientLanguage.Japanese => [], + ClientLanguage.English => EnglishExcludedWords, + ClientLanguage.German => CommonExcludedWords, + ClientLanguage.French => CommonExcludedWords, + _ => [], + }); + } + + /// + /// Converts the input string to uppercase based on specified options like capitalizing the first character, + /// normalizing vowels, and excluding certain words based on the selected language. + /// + /// The input string to be converted to uppercase. + /// Whether to capitalize only the first character of the string. + /// Whether to capitalize the first letter of each word. + /// Whether to normalize vowels to uppercase if they appear at the beginning of a word. + /// A list of words to exclude from being capitalized. Words in this list will remain lowercase. + /// A new string with the appropriate characters converted to uppercase. + /// This is a C# implementation of Client::System::String::Utf8String.ToUpper. + public static string ToUpper(this string input, bool firstCharOnly, bool everyWord, bool normalizeVowels, ReadOnlySpan excludedWords) + { + if (string.IsNullOrEmpty(input)) + return input; + + var builder = new StringBuilder(input); + var isWordBeginning = true; + var length = firstCharOnly && !everyWord ? 1 : builder.Length; + + for (var i = 0; i < length; i++) + { + var ch = builder[i]; + + if (ch == ' ') + { + isWordBeginning = true; + continue; + } + + if (firstCharOnly && !isWordBeginning) + continue; + + // Basic ASCII a-z + if (ch >= 'a' && ch <= 'z') + { + var substr = builder.ToString(i, builder.Length - i); + var isExcluded = false; + + // Do not exclude words at the beginning + if (i > 0) + { + foreach (var excludedWord in excludedWords) + { + if (substr.StartsWith(excludedWord + " ", StringComparison.OrdinalIgnoreCase)) + { + isExcluded = true; + break; + } + } + } + + if (!isExcluded) + { + builder[i] = char.ToUpperInvariant(ch); + } + } + + // Special œ → Œ + else if (ch == 'œ') + { + builder[i] = 'Œ'; + } + + // Characters with accents + else if (ch >= 'à' && ch <= 'ý' && ch != '÷') + { + builder[i] = char.ToUpperInvariant(ch); + } + + // Normalize vowels with accents + else if (normalizeVowels && isWordBeginning) + { + if ("àáâãäå".Contains(ch)) + { + builder[i] = 'A'; + } + else if ("èéêë".Contains(ch)) + { + builder[i] = 'E'; + } + else if ("ìíîï".Contains(ch)) + { + builder[i] = 'I'; + } + else if ("òóôõö".Contains(ch)) + { + builder[i] = 'O'; + } + else if ("ùúûü".Contains(ch)) + { + builder[i] = 'U'; + } + } + + isWordBeginning = false; + } + + return builder.ToString(); + } }