mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-07-15 15:19:57 +00:00
@@ -34,9 +34,9 @@ public class EntryPoint
|
||||
{
|
||||
Environment.SetEnvironmentVariable("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", null, EnvironmentVariableTarget.Process);
|
||||
Environment.SetEnvironmentVariable("ASPNETCORE_PREVENTHOSTINGSTARTUP", "true", EnvironmentVariableTarget.Process);
|
||||
|
||||
#if DEBUG
|
||||
ConsoleExtensions.AllocateAnsiConsole();
|
||||
|
||||
#endif
|
||||
var port = FindAvailablePort(StartPort);
|
||||
var app = CreateApplication(port);
|
||||
var runTask = Task.Run(() => StartServer(app), CancellationTokenSource.Token);
|
||||
|
||||
@@ -128,11 +128,11 @@ internal static unsafe partial class GWCA
|
||||
|
||||
// GW::RegisterLogHandler
|
||||
[LibraryImport(DllName, EntryPoint = "?RegisterLogHandler@GW@@YAXP6AXPAXW4LogLevel@1@PBD2I2@Z0@Z")]
|
||||
internal static partial void RegisterLogHandler(nint callback);
|
||||
internal static partial void RegisterLogHandler(nint callback1, nint ptr2);
|
||||
|
||||
// GW::RegisterPanicHandler
|
||||
[LibraryImport(DllName, EntryPoint = "?RegisterPanicHandler@GW@@YAXP6AXPAXPBD1I1@Z0@Z")]
|
||||
internal static partial void RegisterPanicHandler(nint callback);
|
||||
internal static partial void RegisterPanicHandler(nint callback1, nint ptr2);
|
||||
|
||||
// GW::Terminate
|
||||
[LibraryImport(DllName, EntryPoint = "?Terminate@GW@@YAXXZ")]
|
||||
@@ -2041,7 +2041,7 @@ internal static unsafe partial class GWCA
|
||||
// GW::TabsFrame::AddTab
|
||||
[LibraryImport(DllName, EntryPoint = "?AddTab@TabsFrame@GW@@QAEPAUFrame@UI@2@PB_WIIP6AXPAUInteractionMessage@42@PAX2@Z2@Z")]
|
||||
[UnmanagedCallConv(CallConvs = [typeof(CallConvThiscall)])]
|
||||
internal static partial global::Daybreak.API.Interop.GuildWars.Frame* AddTab(nint self, ushort* ptr2, uint value3, uint value4, nint callback5);
|
||||
internal static partial global::Daybreak.API.Interop.GuildWars.Frame* AddTab(nint self, ushort* ptr2, uint value3, uint value4, nint callback5, nint ptr6);
|
||||
|
||||
// GW::TabsFrame::ChooseTab
|
||||
[LibraryImport(DllName, EntryPoint = "?ChooseTab@TabsFrame@GW@@QAE_NI@Z")]
|
||||
@@ -2171,11 +2171,11 @@ internal static unsafe partial class GWCA
|
||||
// GW::UI::AddFrameUIInteractionCallback
|
||||
[LibraryImport(DllName, EntryPoint = "?AddFrameUIInteractionCallback@UI@GW@@YA_NPAUFrame@12@P6AXPAUInteractionMessage@12@PAX2@Z2@Z")]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static partial bool AddFrameUIInteractionCallback(global::Daybreak.API.Interop.GuildWars.Frame* frame1, nint callback2);
|
||||
internal static partial bool AddFrameUIInteractionCallback(global::Daybreak.API.Interop.GuildWars.Frame* frame1, nint callback2, nint ptr3);
|
||||
|
||||
// GW::UI::AsyncDecodeStr
|
||||
[LibraryImport(DllName, EntryPoint = "?AsyncDecodeStr@UI@GW@@YAXPB_WP6AXPAX0@Z1W4Language@Constants@2@@Z")]
|
||||
internal static partial void AsyncDecodeStr(ushort* ptr1, nint callback2);
|
||||
internal static partial void AsyncDecodeStr(ushort* ptr1, nint callback2, nint ptr3, global::Daybreak.API.Interop.GuildWars.Language language4);
|
||||
|
||||
// GW::UI::AsyncDecodeStr
|
||||
[LibraryImport(DllName, EntryPoint = "?AsyncDecodeStr@UI@GW@@YAXPB_WPA_WI@Z")]
|
||||
@@ -2188,7 +2188,7 @@ internal static unsafe partial class GWCA
|
||||
|
||||
// GW::UI::CreateUIComponent
|
||||
[LibraryImport(DllName, EntryPoint = "?CreateUIComponent@UI@GW@@YAIIIIP6AXPAUInteractionMessage@12@PAX1@Z1PB_W@Z")]
|
||||
internal static partial uint CreateUIComponent(uint value1, uint value2, uint value3, nint callback4);
|
||||
internal static partial uint CreateUIComponent(uint value1, uint value2, uint value3, nint callback4, nint ptr5, ushort* ptr6);
|
||||
|
||||
// GW::UI::Default_UICallback | interactionMessage1: TODO: map struct GW::UI::InteractionMessage
|
||||
// [LibraryImport(DllName, EntryPoint = "?Default_UICallback@UI@GW@@YA_NPAUInteractionMessage@12@PAX1@Z")]
|
||||
|
||||
@@ -109,7 +109,7 @@ public sealed class UIContextService(ILogger<UIContextService> logger)
|
||||
|
||||
public unsafe void SendMessage(UIMessage message, nuint wParam, nuint lParam) => GWCA.GW.UI.SendUIMessage(message, (void*)wParam, (nint)lParam);
|
||||
|
||||
public unsafe Task<string> AsyncDecodeStringAsync(ushort* encodedString, CancellationToken cancellationToken = default)
|
||||
public unsafe Task<string> AsyncDecodeStringAsync(ushort* encodedString, Language language = Language.Unknown, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<string>();
|
||||
cancellationToken.Register(() => taskCompletionSource.TrySetCanceled(cancellationToken));
|
||||
@@ -144,17 +144,18 @@ public sealed class UIContextService(ILogger<UIContextService> logger)
|
||||
this.prevent_GC_callbacks[callback] = true;
|
||||
|
||||
var funcPtr = Marshal.GetFunctionPointerForDelegate(callback);
|
||||
GWCA.GW.UI.AsyncDecodeStr(encodedString, funcPtr);
|
||||
// GWCA's AsyncDecodeStr handles language switching internally (sets language, decodes, restores)
|
||||
GWCA.GW.UI.AsyncDecodeStr(encodedString, funcPtr, 0, language);
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
public unsafe Task<string> AsyncDecodeStringAsync(string encodedString, CancellationToken cancellationToken = default)
|
||||
public unsafe Task<string> AsyncDecodeStringAsync(string encodedString, Language language = Language.Unknown, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Pin the string and convert to ushort*
|
||||
fixed (char* encodedPtr = encodedString)
|
||||
{
|
||||
return this.AsyncDecodeStringAsync((ushort*)encodedPtr, cancellationToken);
|
||||
return this.AsyncDecodeStringAsync((ushort*)encodedPtr, language, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,17 +83,21 @@ public sealed class InventoryService(
|
||||
return default;
|
||||
}
|
||||
|
||||
var decodedItemTuples = await Task.WhenAll(itemTuples.Select(async tuple =>
|
||||
// Decode strings sequentially to avoid race conditions with the game's TextParser language field.
|
||||
// Parallel decoding causes crashes because multiple operations race to set/restore the language.
|
||||
var decodedItemTuples = new List<(BagType Type, List<(uint ModelId, string EncodedName, string? DecodedName, string EncodedSingleName, string? DecodedSingleName, string EncodedCompleteName, string? DecodedCompleteName, bool Inscribable, int Quantity, uint[] Modifiers, ItemType ItemType)> Items)>();
|
||||
foreach (var tuple in itemTuples)
|
||||
{
|
||||
var decodedItems = await Task.WhenAll(tuple.Item2.Select(async item =>
|
||||
var decodedItems = new List<(uint ModelId, string EncodedName, string? DecodedName, string EncodedSingleName, string? DecodedSingleName, string EncodedCompleteName, string? DecodedCompleteName, bool Inscribable, int Quantity, uint[] Modifiers, ItemType ItemType)>();
|
||||
foreach (var item in tuple.Item2)
|
||||
{
|
||||
var decodedName = await this.uIService.DecodeString(item.EncodedName, Language.English, cancellationToken);
|
||||
var decodedCompleteName = await this.uIService.DecodeString(item.EncodedCompleteName, Language.English, cancellationToken);
|
||||
var decodedSingleName = await this.uIService.DecodeString(item.EncodedSingleName, Language.English, cancellationToken);
|
||||
return (item.ModelId, item.EncodedName, DecodedName: decodedName, item.EncodedSingleName, DecodedSingleName: decodedSingleName, item.EncodedCompleteName, DecodedCompleteName: decodedCompleteName, item.Inscribable, item.Quantity, item.Modifiers, item.ItemType);
|
||||
}));
|
||||
return (tuple.Type, Items: decodedItems.ToList());
|
||||
}));
|
||||
decodedItems.Add((item.ModelId, item.EncodedName, decodedName, item.EncodedSingleName, decodedSingleName, item.EncodedCompleteName, decodedCompleteName, item.Inscribable, item.Quantity, item.Modifiers, item.ItemType));
|
||||
}
|
||||
decodedItemTuples.Add((tuple.Type, decodedItems));
|
||||
}
|
||||
|
||||
return new InventoryInformation(
|
||||
Bags: [.. decodedItemTuples.Select(tuple =>
|
||||
|
||||
@@ -8,12 +8,10 @@ namespace Daybreak.API.Services;
|
||||
|
||||
public sealed class UIService(
|
||||
GameThreadService gameThreadService,
|
||||
GameContextService gameContextService,
|
||||
UIContextService uIContextService,
|
||||
ILogger<UIService> logger)
|
||||
{
|
||||
private readonly GameThreadService gameThreadService = gameThreadService;
|
||||
private readonly GameContextService gameContextService = gameContextService;
|
||||
private readonly UIContextService uIContextService = uIContextService;
|
||||
private readonly ILogger<UIService> logger = logger;
|
||||
|
||||
@@ -90,62 +88,8 @@ public sealed class UIService(
|
||||
|
||||
public async Task<string?> DecodeString(string encoded, Language language, CancellationToken cancellationToken)
|
||||
{
|
||||
var scopedLogger = this.logger.CreateScopedLogger();
|
||||
var prevLanguage = await this.gameThreadService.QueueOnGameThread<Language?>(() =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var gameContext = this.gameContextService.GetGameContext();
|
||||
if (gameContext.IsNull)
|
||||
{
|
||||
scopedLogger.LogError("Game context is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
var textParser = gameContext.Pointer->TextParserContext;
|
||||
if (textParser is null)
|
||||
{
|
||||
scopedLogger.LogError("Text parser context is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
var prevLanguage = textParser->Language;
|
||||
if (language is not Language.Unknown)
|
||||
{
|
||||
textParser->Language = language;
|
||||
}
|
||||
|
||||
return prevLanguage;
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
var decoded = await this.uIContextService.AsyncDecodeStringAsync(encoded, cancellationToken);
|
||||
await this.gameThreadService.QueueOnGameThread(() =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var gameContext = this.gameContextService.GetGameContext();
|
||||
if (gameContext.IsNull)
|
||||
{
|
||||
scopedLogger.LogError("Game context is null");
|
||||
return;
|
||||
}
|
||||
|
||||
var textParser = gameContext.Pointer->TextParserContext;
|
||||
if (textParser is null)
|
||||
{
|
||||
scopedLogger.LogError("Text parser context is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevLanguage.HasValue)
|
||||
{
|
||||
textParser->Language = prevLanguage.Value;
|
||||
}
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
return decoded;
|
||||
// GWCA's AsyncDecodeStr handles language switching internally (sets language, decodes, restores)
|
||||
return await this.uIContextService.AsyncDecodeStringAsync(encoded, language, cancellationToken);
|
||||
}
|
||||
|
||||
private static ControlAction GetUIActionFromFrameLabel(string frameLabel)
|
||||
|
||||
@@ -450,10 +450,22 @@ internal sealed class MsvcDemangler
|
||||
if (pos < s.Length && s[pos] >= 'A' && s[pos] <= 'Z') pos++;
|
||||
// Return type
|
||||
ReadType(s, ref pos, nameTable);
|
||||
// Params until @ or Z
|
||||
// Params until @Z (function pointer terminator)
|
||||
while (pos < s.Length)
|
||||
{
|
||||
if (s[pos] is '@' or 'Z') { pos++; break; }
|
||||
// Function pointers are terminated by @Z
|
||||
if (s[pos] == '@')
|
||||
{
|
||||
pos++; // skip @
|
||||
if (pos < s.Length && s[pos] == 'Z')
|
||||
{
|
||||
pos++; // skip Z - this terminates the function pointer, not the outer param list
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Single Z also terminates (void params case)
|
||||
if (s[pos] == 'Z') { pos++; break; }
|
||||
|
||||
ReadType(s, ref pos, nameTable);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ public enum ItemModifierIdentifier : uint
|
||||
Armor1 = 0x27B, // Armor value (arg1)
|
||||
Armor2 = 0x23C, // Armor value (arg1)
|
||||
Energy = 0x27C, // Energy value (arg1)
|
||||
Upgrade1 = 0x21E, // Suffix/Prefix/Inscription upgrade (upgrade id)
|
||||
Upgrade2 = 0x240, // Suffix/Prefix/Inscription upgrade (upgrade id)
|
||||
UpgradeSuffix = 0x21E, // Suffix upgrade (upgrade id)
|
||||
UpgradePrefix = 0x240, // Prefix upgrade (upgrade id)
|
||||
UpgradeSuffix2 = 0x27E, // Suffix upgrade. Param: 8=Minor, 9=Major, A=Superior (upgrade id)
|
||||
UpgradeSuffix3 = 0x253, // Suffix rule. This is normally not correct but for now we'll use this to map runes that alter stats such as hp or energy
|
||||
OfTheProfession = 0x28A, // +5 to primary of profession (arg2) (arg1)
|
||||
DamageType = 0x24B, // Damage type (arg1)
|
||||
|
||||
@@ -104,5 +106,7 @@ public enum ItemModifierIdentifier : uint
|
||||
public enum ItemModifierParam : uint
|
||||
{
|
||||
LabelInName = 0x0,
|
||||
Description = 0x8
|
||||
Description = 0x8,
|
||||
Superior = 0x9,
|
||||
Major = 0xA,
|
||||
}
|
||||
|
||||
@@ -85,8 +85,10 @@ public abstract class ItemProperty
|
||||
ItemModifierIdentifier.Armor1 => new ArmorProperty { Armor =(int)m.Argument1 },
|
||||
ItemModifierIdentifier.Armor2 => new ArmorProperty { Armor = (int)m.Argument1 },
|
||||
ItemModifierIdentifier.Energy => new EnergyProperty { Energy = (int)m.Argument1 },
|
||||
ItemModifierIdentifier.Upgrade1 => ParseUpgradeProperty(m),
|
||||
ItemModifierIdentifier.Upgrade2 => ParseUpgradeProperty(m),
|
||||
ItemModifierIdentifier.UpgradeSuffix => ParseUpgradeProperty(m, type: ItemUpgradeType.Suffix),
|
||||
ItemModifierIdentifier.UpgradePrefix => ParseUpgradeProperty(m, type: ItemUpgradeType.Prefix),
|
||||
ItemModifierIdentifier.UpgradeSuffix2 => ParseUpgradeProperty(m, type: ItemUpgradeType.Suffix, flag: (ItemUpgradeFlag)m.Param),
|
||||
ItemModifierIdentifier.UpgradeSuffix3 => ParseUpgradeProperty(m, ItemUpgradeType.Suffix, flag: ItemUpgradeFlag.Stat),
|
||||
ItemModifierIdentifier.OfTheProfession => ParseOfTheProfessionProperty(m),
|
||||
ItemModifierIdentifier.DamageType => new DamageTypeProperty { DamageType = (DamageType)m.Argument1 },
|
||||
|
||||
@@ -176,9 +178,9 @@ public abstract class ItemProperty
|
||||
return new OfTheProfessionProperty { Profession = (attribute.Profession ?? Profession.None).Name ?? string.Empty, Attribute = attribute.Name ?? string.Empty, Value = (int)m.Argument2 };
|
||||
}
|
||||
|
||||
private static ItemProperty ParseUpgradeProperty(ItemModifier m)
|
||||
private static ItemProperty ParseUpgradeProperty(ItemModifier m, ItemUpgradeType type = ItemUpgradeType.Unknown, ItemUpgradeFlag flag = ItemUpgradeFlag.Default)
|
||||
{
|
||||
if (!ItemUpgrade.TryParse((int)m.UpgradeId, out var upgrade) ||
|
||||
if (!ItemUpgrade.TryParse((int)m.UpgradeId, type, flag, out var upgrade) ||
|
||||
upgrade == ItemUpgrade.Unknown)
|
||||
{
|
||||
return new UnknownUpgradeProperty { RawModifier = m };
|
||||
|
||||
@@ -274,29 +274,28 @@ public sealed class ItemUpgrade
|
||||
public static readonly ItemUpgrade Windwalker = new(0x0202, "Windwalker", ItemUpgradeType.Prefix);
|
||||
public static readonly ItemUpgrade Forsaken = new(0x0203, "Forsaken", ItemUpgradeType.Prefix);
|
||||
public static readonly ItemUpgrade Centurions = new(0x0207, "Centurion's", ItemUpgradeType.Prefix);
|
||||
public static readonly ItemUpgrade OfAttunement = new(0x0211, "of Attunement", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfRecovery = new(0x0213, "of Recovery", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfRestoration = new(0x0214, "of Restoration", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfClarity = new(0x0215, "of Clarity", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfPurity = new(0x0216, "of Purity", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMinorVigor = new(0x00FF, "of Minor Vigor", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMinorVigor2 = new(0x00C2, "of Minor Vigor", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfSuperiorVigor = new(0x0101, "of Superior Vigor", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMajorVigor = new(0x0100, "of Major Vigor", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfVitae = new(0x0212, "of Vitae", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMinorAbsorption = new(0x00FC, "of Minor Absorption", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfAttunement = new(0x0423, "of Attunement", ItemUpgradeType.Suffix, ItemUpgradeFlag.Stat);
|
||||
public static readonly ItemUpgrade OfRecovery = new(0x0427, "of Recovery", ItemUpgradeType.Suffix, ItemUpgradeFlag.Stat);
|
||||
public static readonly ItemUpgrade OfRestoration = new(0x0429, "of Restoration", ItemUpgradeType.Suffix, ItemUpgradeFlag.Stat);
|
||||
public static readonly ItemUpgrade OfClarity = new(0x042B, "of Clarity", ItemUpgradeType.Suffix, ItemUpgradeFlag.Stat);
|
||||
public static readonly ItemUpgrade OfPurity = new(0x042D, "of Purity", ItemUpgradeType.Suffix, ItemUpgradeFlag.Stat);
|
||||
public static readonly ItemUpgrade OfMinorVigor = new(0x02C2, "of Minor Vigor", ItemUpgradeType.Suffix, ItemUpgradeFlag.Base);
|
||||
public static readonly ItemUpgrade OfSuperiorVigor = new(0x02C2, "of Superior Vigor", ItemUpgradeType.Suffix, ItemUpgradeFlag.Superior);
|
||||
public static readonly ItemUpgrade OfMajorVigor = new(0x02C2, "of Major Vigor", ItemUpgradeType.Suffix, ItemUpgradeFlag.Major);
|
||||
public static readonly ItemUpgrade OfVitae = new(0x0425, "of Vitae", ItemUpgradeType.Suffix, ItemUpgradeFlag.Stat);
|
||||
public static readonly ItemUpgrade OfMinorAbsorption = new(0x02EA, "of Minor Absorption", ItemUpgradeType.Suffix, ItemUpgradeFlag.Base);
|
||||
public static readonly ItemUpgrade OfMinorTactics = new(0x1501, "of Minor Tactics", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMinorStrength = new(0x1101, "of Minor Strength", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMinorAxeMastery = new(0x1201, "of Minor Axe Mastery", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMinorHammerMastery = new(0x1301, "of Minor Hammer Mastery", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMinorSwordsmanship = new(0x1401, "of Minor Swordsmanship", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMajorAbsorption = new(0x00FD, "of Major Absorption", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMajorAbsorption = new(0x02EA, "of Major Absorption", ItemUpgradeType.Suffix, ItemUpgradeFlag.Major);
|
||||
public static readonly ItemUpgrade OfMajorTactics = new(0x1502, "of Major Tactics", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMajorStrength = new(0x1102, "of Major Strength", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMajorAxeMastery = new(0x1202, "of Major Axe Mastery", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMajorHammerMastery = new(0x1302, "of Major Hammer Mastery", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfMajorSwordsmanship = new(0x1402, "of Major Swordsmanship", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfSuperiorAbsorption = new(0x00FE, "of Superior Absorption", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfSuperiorAbsorption = new(0x02EA, "of Superior Absorption", ItemUpgradeType.Suffix, ItemUpgradeFlag.Superior);
|
||||
public static readonly ItemUpgrade OfSuperiorTactics = new(0x1503, "of Superior Tactics", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfSuperiorStrength = new(0x1103, "of Superior Strength", ItemUpgradeType.Suffix);
|
||||
public static readonly ItemUpgrade OfSuperiorAxeMastery = new(0x1203, "of Superior Axe Mastery", ItemUpgradeType.Suffix);
|
||||
@@ -758,7 +757,6 @@ public sealed class ItemUpgrade
|
||||
OfClarity,
|
||||
OfPurity,
|
||||
OfMinorVigor,
|
||||
OfMinorVigor2,
|
||||
OfSuperiorVigor,
|
||||
OfMajorVigor,
|
||||
OfVitae,
|
||||
@@ -969,6 +967,47 @@ public sealed class ItemUpgrade
|
||||
return upgrade is not null;
|
||||
}
|
||||
|
||||
public static bool TryParse(int id, ItemUpgradeType type, [NotNullWhen(true)] out ItemUpgrade? upgrade)
|
||||
{
|
||||
if (type is ItemUpgradeType.Unknown)
|
||||
{
|
||||
return TryParse(id, out upgrade);
|
||||
}
|
||||
|
||||
upgrade = ItemUpgrades.FirstOrDefault(u => u.Id == id && u.Type == type);
|
||||
return upgrade is not null;
|
||||
}
|
||||
|
||||
public static bool TryParse(int id, ItemUpgradeFlag flag, [NotNullWhen(true)] out ItemUpgrade? upgrade)
|
||||
{
|
||||
if (flag is ItemUpgradeFlag.Default)
|
||||
{
|
||||
return TryParse(id, out upgrade);
|
||||
}
|
||||
|
||||
upgrade = ItemUpgrades.FirstOrDefault(u => u.Id == id && u.Flag == flag);
|
||||
return upgrade is not null;
|
||||
}
|
||||
|
||||
public static bool TryParse(int id, ItemUpgradeType type, ItemUpgradeFlag flag, [NotNullWhen(true)] out ItemUpgrade? upgrade)
|
||||
{
|
||||
if (flag is ItemUpgradeFlag.Default && type is ItemUpgradeType.Unknown)
|
||||
{
|
||||
return TryParse(id, out upgrade);
|
||||
}
|
||||
else if (flag is ItemUpgradeFlag.Default)
|
||||
{
|
||||
return TryParse(id, type, out upgrade);
|
||||
}
|
||||
else if (type is ItemUpgradeType.Unknown)
|
||||
{
|
||||
return TryParse(id, flag, out upgrade);
|
||||
}
|
||||
|
||||
upgrade = ItemUpgrades.FirstOrDefault(u => u.Id == id && u.Type == type && u.Flag == flag);
|
||||
return upgrade is not null;
|
||||
}
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
[JsonConverter(typeof(HexIntConverter))]
|
||||
public int Id { get; }
|
||||
@@ -979,10 +1018,14 @@ public sealed class ItemUpgrade
|
||||
[JsonPropertyName("type")]
|
||||
public ItemUpgradeType Type { get; }
|
||||
|
||||
private ItemUpgrade(int id, string name, ItemUpgradeType type)
|
||||
[JsonPropertyName("flag")]
|
||||
public ItemUpgradeFlag Flag { get; }
|
||||
|
||||
private ItemUpgrade(int id, string name, ItemUpgradeType type, ItemUpgradeFlag flag = ItemUpgradeFlag.Default)
|
||||
{
|
||||
this.Id = id;
|
||||
this.Name = name;
|
||||
this.Type = type;
|
||||
this.Flag = flag;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Daybreak.Shared.Models.Guildwars;
|
||||
|
||||
public enum ItemUpgradeFlag : uint
|
||||
{
|
||||
Default = 0x00,
|
||||
Base = 0x08,
|
||||
Major = 0x09,
|
||||
Superior = 0x0A,
|
||||
|
||||
// Not a real flag, just used for the stat runes
|
||||
Stat = 0x02
|
||||
}
|
||||
Reference in New Issue
Block a user