Update GWCA (#1484)

* Improve GWCA bindings generator based on headers

* Move all structs to gwca

* Progress

* Complete parser work

* More generator adjustments

* GWCA fixes

* GWCA improvements

* GWCA fixes

* GWCA fixes

* GWCA fixes

* GWCA fixes

* Fix API usage of GWCA

* Update GWCA

---------

Co-authored-by: Alexandru Macocian <amacocian@microsoft.com>
This commit is contained in:
2026-02-26 13:22:38 -08:00
parent 68f241989a
commit 97fed608d2
159 changed files with 33517 additions and 3815 deletions
@@ -1,19 +0,0 @@
using Daybreak.API.Interop;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Daybreak.API.Converters;
public sealed class PointerValueConverter : JsonConverter<PointerValue>
{
public override PointerValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var s = reader.GetString() ?? throw new JsonException("Expected string");
return PointerValue.Parse(s);
}
public override void Write(Utf8JsonWriter writer, PointerValue value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
@@ -1,17 +1,18 @@
using Daybreak.API.Interop.GuildWars;
using Daybreak.Shared.Models.Api;
using ZLinq;
namespace Daybreak.API.Extensions;
public static class AttributeContextExtensions
{
public static List<AttributeEntry> GetAttributeEntryList(this AttributeContext[] attributes)
public static IEnumerable<AttributeEntry> GetAttributeEntryList(this AttributeStructArray54 attributes)
{
return attributes
.AsValueEnumerable()
.Take(45) // There are only 45 maximum attributes in game
.Where(a => a.LevelBase > 0 && a.Level >= a.LevelBase) // Select only attributes with points in them
.Select(a => new AttributeEntry(a.Id, a.LevelBase, a.Level)).ToList();
foreach (var attribute in attributes)
{
if (attribute.LevelBase > 0 && attribute.Level >= attribute.LevelBase)
{
yield return new AttributeEntry((uint)attribute.Id, attribute.LevelBase, attribute.Level);
}
}
}
}
@@ -0,0 +1,36 @@
using Daybreak.API.Interop.GuildWars;
namespace Daybreak.API.Extensions;
public static class CharacterInformationExtensions
{
public unsafe static Profession GetPrimaryProfession(this CharacterInformation characterInformation)
{
return (Profession)((characterInformation.Props[2] >> 20) & 0xF);
}
public unsafe static Profession GetSecondaryProfession(this CharacterInformation characterInformation)
{
return (Profession)((characterInformation.Props[7] >> 10) & 0xF);
}
public unsafe static uint GetLevel(this CharacterInformation characterInformation)
{
return (characterInformation.Props[7] >> 4) & 0x3F;
}
public unsafe static Campaign GetCampaign(this CharacterInformation characterInformation)
{
return (Campaign)(characterInformation.Props[7] & 0xF);
}
public unsafe static MapID GetMapId(this CharacterInformation characterInformation)
{
return (MapID)((characterInformation.Props[0] >> 16) & 0xFFFF);
}
public unsafe static bool IsPvP(this CharacterInformation characterInformation)
{
return ((characterInformation.Props[7] >> 9) & 0x1) == 0x1;
}
}
@@ -1,4 +1,5 @@
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using System.Diagnostics.CodeAnalysis;
namespace Daybreak.API.Extensions;
@@ -11,21 +12,21 @@ public static unsafe class GameContextExtensions
{
playerId = 0;
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null ||
gameContext.Pointer->WorldContext->PlayerControlledChar is null)
gameContext.Pointer->World is null ||
gameContext.Pointer->World->PlayerControlledChar is null)
{
return false;
}
playerId = gameContext.Pointer->WorldContext->PlayerControlledChar->AgentId;
playerId = gameContext.Pointer->World->PlayerControlledChar->AgentId;
return true;
}
public static bool TryGetBuildContext(
this WrappedPointer<GameContext> gameContext,
[NotNullWhen(true)] out GuildWarsArray<SkillbarContext>? skillbars,
[NotNullWhen(true)] out GuildWarsArray<SkillbarData>? skillbars,
[NotNullWhen(true)] out GuildWarsArray<PartyAttribute>? attributes,
[NotNullWhen(true)] out GuildWarsArray<ProfessionsContext>? professions,
[NotNullWhen(true)] out GuildWarsArray<ProfessionState>? professions,
[NotNullWhen(true)] out GuildWarsArray<uint>? unlockedSkills)
{
skillbars = default;
@@ -33,15 +34,15 @@ public static unsafe class GameContextExtensions
professions = default;
unlockedSkills = default;
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null)
gameContext.Pointer->World is null)
{
return false;
}
skillbars = gameContext.Pointer->WorldContext->Skillbars;
attributes = gameContext.Pointer->WorldContext->Attributes;
professions = gameContext.Pointer->WorldContext->Professions;
unlockedSkills = gameContext.Pointer->WorldContext->UnlockedCharacterSkills;
skillbars = gameContext.Pointer->World->Skillbar.Value;
attributes = gameContext.Pointer->World->Attributes.Value;
professions = gameContext.Pointer->World->PartyProfessionStates;
unlockedSkills = gameContext.Pointer->World->UnlockedCharacterSkills;
return true;
}
@@ -57,15 +58,16 @@ public static unsafe class GameContextExtensions
heroes = default;
henchmen = default;
if (gameContext.IsNull ||
gameContext.Pointer->PartyContext is null)
gameContext.Pointer->Party is 0)
{
return false;
}
partyId = gameContext.Pointer->PartyContext->PlayerParty->PartyId;
players = gameContext.Pointer->PartyContext->PlayerParty->Players;
heroes = gameContext.Pointer->PartyContext->PlayerParty->Heroes;
henchmen = gameContext.Pointer->PartyContext->PlayerParty->Henchmen;
var partyInfo = GWCA.GW.PartyMgr.GetPartyInfo(0);
partyId = partyInfo->PartyId;
players = partyInfo->Players;
heroes = partyInfo->Heroes;
henchmen = partyInfo->Henchmen;
return true;
}
@@ -75,27 +77,27 @@ public static unsafe class GameContextExtensions
{
heroFlags = default;
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null)
gameContext.Pointer->World is null)
{
return false;
}
heroFlags = gameContext.Pointer->WorldContext->HeroFlags;
heroFlags = gameContext.Pointer->World->HeroFlags.Value;
return true;
}
public static bool TryGetAccountContext(
this WrappedPointer<GameContext> gameContext,
out WrappedPointer<AccountGameContext> accountContext)
out WrappedPointer<AccountContext> accountContext)
{
accountContext = default;
if (gameContext.IsNull ||
gameContext.Pointer->AccountContext is null)
gameContext.Pointer->Account is null)
{
return false;
}
accountContext = gameContext.Pointer->AccountContext;
accountContext = gameContext.Pointer->Account;
return true;
}
}
@@ -0,0 +1,16 @@
using Daybreak.API.Interop.GuildWars;
namespace Daybreak.API.Extensions;
public static class TitleExtensions
{
public static bool IsPercentageBased(this Title title)
{
return (title.Props & 1) != 0;
}
public static bool HasTiers(this Title title)
{
return (title.Props & 3) == 2;
}
}
+34
View File
@@ -0,0 +1,34 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1AC)]
[GWCAEquivalent("Frame")]
public readonly struct Frame
{
[FieldOffset(0x0008)]
public readonly uint FrameLayout;
[FieldOffset(0x0018)]
public readonly uint VisibilityFlags;
[FieldOffset(0x0020)]
public readonly uint Type;
[FieldOffset(0x0024)]
public readonly uint TemplateType;
[FieldOffset(0x00b8)]
public readonly uint ChildOffsetId;
[FieldOffset(0x00bc)]
public readonly uint FrameId;
[FieldOffset(0x018C)]
public readonly uint FrameState;
public bool IsCreated => (this.FrameState & 0x4) != 0;
public bool IsHidden => (this.FrameState & 0x200) != 0;
public bool IsVisible => !this.IsHidden;
public bool IsDIsabled => (this.FrameState & 0x10) != 0;
}
-26
View File
@@ -1,26 +0,0 @@
using System.Core.Extensions;
namespace Daybreak.API.Interop;
public sealed class GWAddressCache(Func<nuint> provider)
{
private readonly Func<nuint> provider = provider.ThrowIfNull();
private nuint? cachedAddress;
public nuint? GetAddress()
{
if (this.cachedAddress.HasValue)
{
return this.cachedAddress.Value;
}
var addr = this.provider();
if (addr is 0x0)
{
return null;
}
this.cachedAddress = addr;
return addr;
}
}
+16892 -692
View File
File diff suppressed because it is too large Load Diff
-19
View File
@@ -1,19 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop;
public sealed class GWDelegateCache<TDelegate>(GWAddressCache cache)
where TDelegate : Delegate
{
public GWAddressCache Cache { get; } = cache;
public TDelegate? GetDelegate()
{
if (this.Cache.GetAddress() is not nuint address)
{
return default;
}
return Marshal.GetDelegateForFunctionPointer<TDelegate>((nint)address);
}
}
-312
View File
@@ -1,312 +0,0 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop;
public abstract class GWFastCall(GWAddressCache target)
{
public readonly struct Void { }
public GWAddressCache Cache { get; } = target;
public abstract void Initialize();
/// <summary>
/// Builds a thunk for __fastcall with 1 parameter (ECX only).
/// </summary>
protected unsafe static nint BuildFastCallThunk1(nuint target)
{
if (target is 0)
{
return 0;
}
var code = stackalloc byte[10]
{
0x58, // pop eax (ret)
0x59, // pop ecx (arg1)
0x50, // push eax (ret)
0xB8, 0, 0, 0, 0, // mov eax, TARGET
0xFF, 0xE0 // jmp eax
}; // 10 bytes total
const int TARGET_OFFSET = 4;
Unsafe.WriteUnaligned(ref code[TARGET_OFFSET], (uint)target);
var mem = NativeMethods.VirtualAlloc(0, 10, NativeMethods.MEM_COMMIT | NativeMethods.MEM_RESERVE, NativeMethods.PAGE_EXECUTE_READWRITE);
if (mem is 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "VirtualAlloc");
}
Buffer.MemoryCopy(code, (void*)mem, 10, 10);
return mem;
}
/// <summary>
/// Builds a thunk for __fastcall with 2+ parameters (ECX and EDX).
/// </summary>
protected unsafe static nint BuildFastCallThunk2(nuint target)
{
if (target is 0)
{
return 0;
}
var code = stackalloc byte[11]
{
0x58, // pop eax (ret)
0x59, // pop ecx (ctx)
0x5A, // pop edx (edxVal)
0x50, // push eax (ret)
0xB8,0,0,0,0, // mov eax, TARGET
0xFF,0xE0 // jmp eax
}; // 11 bytes total
const int TARGET_OFFSET = 5;
Unsafe.WriteUnaligned(ref code[TARGET_OFFSET], (uint)target);
var mem = NativeMethods.VirtualAlloc(0, 11, NativeMethods.MEM_COMMIT | NativeMethods.MEM_RESERVE, NativeMethods.PAGE_EXECUTE_READWRITE);
if (mem is 0)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "VirtualAlloc");
}
Buffer.MemoryCopy(code, (void*)mem, 11, 11);
return mem;
}
}
public unsafe sealed class GWFastCall<T1, T2>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T2? Invoke(T1 t1)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T2) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, void>)this.func)(t1);
return default;
}
return this.func(t1);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk1(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T3? Invoke(T1 t1, T2 t2)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T3) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, void>)this.func)(t1, t2);
return default;
}
return this.func(t1, t2);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk2(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T4? Invoke(T1 t1, T2 t2, T3 t3)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T4) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, void>)this.func)(t1, t2, t3);
return default;
}
return this.func(t1, t2, t3);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk2(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4, T5>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T5? Invoke(T1 t1, T2 t2, T3 t3, T4 t4)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T5) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, T4, void>)this.func)(t1, t2, t3, t4);
return default;
}
return this.func(t1, t2, t3, t4);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk2(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4, T5, T6>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T6? Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T6) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, void>)this.func)(t1, t2, t3, t4, t5);
return default;
}
return this.func(t1, t2, t3, t4, t5);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk2(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6>)call;
}
}
public unsafe sealed class GWFastCall<T1, T2, T3, T4, T5, T6, T7>(GWAddressCache target) : GWFastCall(target)
{
private delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6, T7> func = null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe T7? Invoke(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
if (this.func is null)
{
this.Initialize();
}
if (this.func is not null)
{
if (typeof(T7) == typeof(Void))
{
((delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6, void>)this.func)(t1, t2, t3, t4, t5, t6);
return default;
}
return this.func(t1, t2, t3, t4, t5, t6);
}
return default;
}
public override void Initialize()
{
if (this.Cache.GetAddress() is not nuint addr ||
addr is 0)
{
return;
}
var call = BuildFastCallThunk2(addr);
this.func = (delegate* unmanaged[Stdcall]<T1, T2, T3, T4, T5, T6, T7>)call;
}
}
-122
View File
@@ -1,122 +0,0 @@
using System.Runtime.InteropServices;
using MinHook;
namespace Daybreak.API.Interop;
/// <summary>
/// A thin convenience wrapper around MinHook that
/// resolves the target address lazily through <see cref="GWAddressCache"/>
/// unwraps an existing “CALL/JMP rel32” stub so we patch the real code
/// exposes <see cref="Original"/> so your detour can chain
/// </summary>
public sealed class GWHook<T>(
GWAddressCache funcAddress,
T detour,
bool bypassPreviousHooks = false) : IHook<T>
where T : Delegate
{
private readonly bool bypassPreviousHooks = bypassPreviousHooks;
private readonly GWAddressCache addressCache = funcAddress ?? throw new ArgumentNullException(nameof(funcAddress));
private readonly T detour = detour ?? throw new ArgumentNullException(nameof(detour));
private readonly SemaphoreSlim semaphore = new(1, 1);
private T? cont; // trampoline returned by MinHook
private HookEngine? engine;
/// <summary>Delegate that calls the next hook / real function.</summary>
public T Continue => this.cont ??
throw new InvalidOperationException("Hook not initialised call EnsureInitialized() first.");
public bool Hooked { get; private set; } = false;
public nuint TargetAddress { get; private set; }
public nuint ContinueAddress { get; private set; }
public nuint DetourAddress { get; private set; }
/// <summary>Installs the hook exactly once (thread-safe).</summary>
public bool EnsureInitialized()
{
if (this.Hooked)
{
return true;
}
this.semaphore.Wait();
try
{
if (this.Hooked)
{
return true;
}
var addr = this.addressCache.GetAddress();
if (addr.HasValue is false ||
addr.Value is 0)
{
return false;
}
var target = this.bypassPreviousHooks
? UnwrapNearBranch(addr.Value)
: addr.Value;
if (target is 0)
{
return false;
}
this.engine = new HookEngine();
this.cont = this.engine.CreateHook((nint)target, this.detour);
this.engine.EnableHooks();
this.TargetAddress = target;
this.ContinueAddress = (nuint)this.cont.Method.MethodHandle.GetFunctionPointer();
this.DetourAddress = (nuint)this.detour.Method.MethodHandle.GetFunctionPointer();
this.Hooked = true;
return true;
}
catch (Exception)
{
return false;
}
finally
{
this.semaphore.Release();
}
}
public void Dispose()
{
this.semaphore.Wait();
try
{
if (!this.Hooked ||
this.engine is null)
{
return;
}
this.engine.DisableHooks();
this.engine.Dispose();
this.cont = null;
this.Hooked = false;
}
finally
{
this.semaphore.Release();
}
}
/// <summary>
/// Follows a <c>CALL rel32</c> (<c>E8</c>) **or** <c>JMP rel32</c> (<c>E9</c>)
/// trampoline that may have been planted by a previous hook.
/// </summary>
private static unsafe nuint UnwrapNearBranch(nuint addr)
{
byte op = Marshal.ReadByte((IntPtr)addr);
if (op != 0xE8 && op != 0xE9) // not CALL/JMP rel32
return addr;
int rel = Marshal.ReadInt32((IntPtr)(addr + 1));
long dst = (long)addr + 5 + rel; // 5-byte instruction
return (nuint)dst;
}
}
@@ -1,27 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xC)]
public readonly struct AccountUnlockedCount
{
[FieldOffset(0x0000)]
public readonly uint Id;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x138)]
[GWCAEquivalent("AccountContext")]
public readonly struct AccountGameContext
{
[FieldOffset(0x0000)]
public readonly GuildWarsArray<AccountUnlockedCount> AccountUnlockedCounts;
[FieldOffset(0x00b4)]
public readonly GuildWarsArray<uint> UnlockedPvpHeroes;
[FieldOffset(0x0124)]
public readonly GuildWarsArray<uint> UnlockedAccountSkills;
[FieldOffset(0x0134)]
public readonly uint AccountFlags;
}
@@ -1,16 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x1C)]
[GWCAEquivalent("AccountInfo")]
public readonly unsafe struct AccountInfoContext
{
public readonly char* AccountName;
public readonly uint Wins;
public readonly uint Losses;
public readonly uint Rating;
public readonly uint QualifierPoints;
public readonly uint Rank;
public readonly uint TournamentRewardPoints;
}
-254
View File
@@ -1,254 +0,0 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum AgentType : uint
{
Living = 0xDB,
Gadget = 0x200,
Item = 0x400
}
[Flags]
public enum AgentModelType : ushort
{
NPC = 0x2000,
Player = 0x3000
}
[Flags]
public enum LivingAgentState : uint
{
None = 0,
InCombatStance = 0x000001,
HasQuest = 0x000002,
Dead = 0x000008,
Female = 0x000200,
HasBossGlow = 0x000400,
HidingCape = 0x001000,
CanBeViewedInPartyWindow = 0x020000,
SpawnedSpirit = 0x040000,
BeingObservedOrPlayer = 0x400000
}
public enum LivingAgentAllegiance : byte
{
Ally_NonAttackable = 0x1,
Neutral = 0x2,
Enemy = 0x3,
Spirit_Pet = 0x4,
Minion = 0x5,
Npc_Minipet = 0x6
};
[Flags]
public enum LivingAgentEffects : uint
{
None = 0,
Bleeding = 0x0001,
Conditioned = 0x0002,
Crippled1 = 0x0008, // Part of the Crippled check
Crippled = 0x000A, // Combined flag for crippled (0x0008 | 0x0002)
Dead = 0x0010,
DeepWound = 0x0020,
Poisoned = 0x0040,
Enchanted = 0x0080,
DegenHexed = 0x0400,
Hexed = 0x0800,
WeaponSpelled = 0x8000
}
public enum LivingAgentModelState : ushort
{
// Idle states
Idle1 = 64,
Idle2 = 68,
Idle3 = 100,
// Casting states
Casting1 = 65,
Casting2 = 581,
// Moving states
Moving1 = 12,
Moving2 = 76,
Moving3 = 204,
// Attacking states
Attacking1 = 96,
Attacking2 = 1088,
Attacking3 = 1120,
// Other states
KnockedDown = 1104
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("AgentEffects")]
public readonly struct AgentEffects
{
public readonly uint AgentId;
public readonly GuildWarsArray<Buff> Buffs;
public readonly GuildWarsArray<Effect> Effects;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("AgentContext")]
public readonly struct AgentGameContext
{
[FieldOffset(0x01AC)]
public readonly uint InstanceTimer;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 196)]
[GWCAEquivalent("Agent")]
public readonly struct AgentContext
{
[FieldOffset(0x0014)]
public readonly uint AgentInstanceTimer;
[FieldOffset(0x002C)]
public readonly uint AgentId;
[FieldOffset(0x0074)]
public readonly GamePos Pos;
[FieldOffset(0x009C)]
public readonly AgentType Type;
[FieldOffset(0x00A0)]
public readonly Vector2 Velocity;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 212)]
[GWCAEquivalent("AgentItem")]
public readonly struct AgentItemContext
{
[FieldOffset(0x00C4)]
public readonly uint OwnerId;
[FieldOffset(0x00C8)]
public readonly uint ItemId;
[FieldOffset(0x00D0)]
public readonly uint ExtraType;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 228)]
[GWCAEquivalent("AgentGadget")]
public readonly struct AgentGadgetContext
{
[FieldOffset(0x00CC)]
public readonly uint ExtraType;
[FieldOffset(0x00D0)]
public readonly uint GadgetId;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1C0)]
[GWCAEquivalent("AgentLiving")]
public readonly unsafe struct AgentLivingContext
{
[FieldOffset(0x00C4)]
public readonly uint OwnerId;
[FieldOffset(0x00F4)]
public readonly ushort AgentType; // All non-player have an identifier. Two of the same mob have the same identifier;
[FieldOffset(0x00F6)]
public readonly AgentModelType AgentModelType;
[FieldOffset(0x0108)]
public readonly TagInfo* Tags;
[FieldOffset(0x010E)]
public readonly byte Primary;
[FieldOffset(0x010F)]
public readonly byte Secondary;
[FieldOffset(0x0110)]
public readonly byte Level;
[FieldOffset(0x0111)]
public readonly byte TeamId;
[FieldOffset(0x0118)]
public readonly float EnergyRegen;
[FieldOffset(0x0120)]
public readonly float Energy;
[FieldOffset(0x0124)]
public readonly uint MaxEnergy;
[FieldOffset(0x012C)]
public readonly float HealthRegen;
[FieldOffset(0x0134)]
public readonly float Health;
[FieldOffset(0x0138)]
public readonly uint MaxHealth;
[FieldOffset(0x013C)]
public readonly uint Effects;
[FieldOffset(0x0158)]
public readonly LivingAgentModelState ModelState;
[FieldOffset(0x015C)]
public readonly LivingAgentState State;
[FieldOffset(0x0184)]
public readonly uint LoginNumber;
[FieldOffset(0x01B5)]
public readonly LivingAgentAllegiance Allegiance;
public readonly bool IsAlive => (!this.State.HasFlag(LivingAgentState.Dead)) && this.Health > 0f;
public readonly bool IsPlayer => this.LoginNumber is not 0;
public readonly bool IsNpc => this.LoginNumber is 0;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 52)]
[GWCAEquivalent("MapAgent")]
public readonly struct MapAgentContext
{
[FieldOffset(0x0000)]
public readonly float CurrentEnergy;
[FieldOffset(0x0004)]
public readonly float MaxEnergy;
[FieldOffset(0x0008)]
public readonly float EnergyRegen;
[FieldOffset(0x000C)]
public readonly uint SkillTimestamp;
[FieldOffset(0x0020)]
public readonly float CurrentHealth;
[FieldOffset(0x0024)]
public readonly float MaxHealth;
[FieldOffset(0x0028)]
public readonly float HealthRegen;
[FieldOffset(0x0030)]
public readonly LivingAgentEffects Effects;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x38)]
[GWCAEquivalent("AgentInfo")]
public readonly unsafe struct AgentInfo
{
[FieldOffset(0x0034)]
public readonly char* NameEncoded;
}
@@ -1,96 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("Attribute")]
public readonly struct AttributeContext
{
public readonly uint Id;
public readonly uint LevelBase;
public readonly uint Level;
public readonly uint DecrementPoints;
public readonly uint IncrementPoints;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("AttributeInfo")]
public readonly struct AttributeInfo
{
public readonly uint ProfessionId;
public readonly uint AttributeId;
public readonly uint NameId;
public readonly uint DescriptionId;
public readonly uint IsPve;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("PartyAttribute")]
public readonly struct PartyAttribute
{
public readonly uint AgentId;
public readonly AttributeContext Attribute0;
public readonly AttributeContext Attribute1;
public readonly AttributeContext Attribute2;
public readonly AttributeContext Attribute3;
public readonly AttributeContext Attribute4;
public readonly AttributeContext Attribute5;
public readonly AttributeContext Attribute6;
public readonly AttributeContext Attribute7;
public readonly AttributeContext Attribute8;
public readonly AttributeContext Attribute9;
public readonly AttributeContext Attribute10;
public readonly AttributeContext Attribute11;
public readonly AttributeContext Attribute12;
public readonly AttributeContext Attribute13;
public readonly AttributeContext Attribute14;
public readonly AttributeContext Attribute15;
public readonly AttributeContext Attribute16;
public readonly AttributeContext Attribute17;
public readonly AttributeContext Attribute18;
public readonly AttributeContext Attribute19;
public readonly AttributeContext Attribute20;
public readonly AttributeContext Attribute21;
public readonly AttributeContext Attribute22;
public readonly AttributeContext Attribute23;
public readonly AttributeContext Attribute24;
public readonly AttributeContext Attribute25;
public readonly AttributeContext Attribute26;
public readonly AttributeContext Attribute27;
public readonly AttributeContext Attribute28;
public readonly AttributeContext Attribute29;
public readonly AttributeContext Attribute30;
public readonly AttributeContext Attribute31;
public readonly AttributeContext Attribute32;
public readonly AttributeContext Attribute33;
public readonly AttributeContext Attribute34;
public readonly AttributeContext Attribute35;
public readonly AttributeContext Attribute36;
public readonly AttributeContext Attribute37;
public readonly AttributeContext Attribute38;
public readonly AttributeContext Attribute39;
public readonly AttributeContext Attribute40;
public readonly AttributeContext Attribute41;
public readonly AttributeContext Attribute42;
public readonly AttributeContext Attribute43;
public readonly AttributeContext Attribute44;
public readonly AttributeContext Attribute45;
public readonly AttributeContext Attribute46;
public readonly AttributeContext Attribute47;
public readonly AttributeContext Attribute48;
public readonly AttributeContext Attribute49;
public readonly AttributeContext Attribute50;
public readonly AttributeContext Attribute51;
public readonly AttributeContext Attribute52;
public readonly AttributeContext Attribute53;
public readonly AttributeContext[] Attributes =>
[
this.Attribute0, this.Attribute1, this.Attribute2, this.Attribute3, this.Attribute4, this.Attribute5, this.Attribute6, this.Attribute7, this.Attribute8, this.Attribute9,
this.Attribute10, this.Attribute11, this.Attribute12, this.Attribute13, this.Attribute14, this.Attribute15, this.Attribute16, this.Attribute17, this.Attribute18, this.Attribute19,
this.Attribute20, this.Attribute21, this.Attribute22, this.Attribute23, this.Attribute24, this.Attribute25, this.Attribute26, this.Attribute27, this.Attribute28, this.Attribute29,
this.Attribute30, this.Attribute31, this.Attribute32, this.Attribute33, this.Attribute34, this.Attribute35, this.Attribute36, this.Attribute37, this.Attribute38, this.Attribute39,
this.Attribute40, this.Attribute41, this.Attribute42, this.Attribute43, this.Attribute44, this.Attribute45, this.Attribute46, this.Attribute47, this.Attribute48, this.Attribute49,
this.Attribute50, this.Attribute51, this.Attribute52, this.Attribute53
];
}
-31
View File
@@ -1,31 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
public enum BagType
{
None,
Inventory,
Equipped,
NotCollected,
Storage,
MaterialsStorage
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("Bag")]
public readonly unsafe struct Bag
{
[FieldOffset(0x0000)]
public readonly BagType Type;
[FieldOffset(0x0004)]
public readonly uint Index;
[FieldOffset(0x000C)]
public readonly uint ContainerItem;
[FieldOffset(0x0010)]
public readonly uint ItemsCount;
[FieldOffset(0x0014)]
public readonly Bag* Bags;
[FieldOffset(0x0018)]
public readonly GuildWarsArray<WrappedPointer<Item>> Items;
}
@@ -1,9 +0,0 @@
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("HeroBehavior")]
public enum Behavior
{
Fight,
Guard,
AvoidCombat
}
@@ -1,51 +0,0 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("CharContext")]
public readonly struct CharContext
{
[FieldOffset(0x0064)]
public readonly Uuid PlayerUuid;
[FieldOffset(0x0074)]
public readonly Array20Char PlayerName;
[FieldOffset(0x0198)]
public readonly uint MapId;
[FieldOffset(0x019C)]
public readonly uint IsExporable;
[FieldOffset(0x01A0)]
public readonly Array24Byte Host;
[FieldOffset(0x01B8)]
public readonly uint PlayerId;
[FieldOffset(0x0220)]
public readonly uint DistrictNumber;
[FieldOffset(0x0224)]
public readonly Language Language;
[FieldOffset(0x0228)]
public readonly uint ObserveMapId;
[FieldOffset(0x022C)]
public readonly uint CurrentMapId;
[FieldOffset(0x0230)]
public readonly uint ObserveMapType;
[FieldOffset(0x0234)]
public readonly uint CurrentMapType;
[FieldOffset(0x02A4)]
public readonly uint PlayerNumber;
[FieldOffset(0x03B8)]
public readonly Array64Char PlayerEmail;
}
@@ -1,23 +0,0 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("CharacterInformation")]
public readonly struct CharInfoContext
{
[FieldOffset(0x0008)]
public readonly Uuid Uuid;
[FieldOffset(0x0018)]
public readonly Array20Char Name;
[FieldOffset(0x0040)]
public readonly Array17Uint Props;
public readonly uint MapId => (this.Props[0] >> 16) & 0xFFFF;
public readonly uint Primary => (this.Props[2] >> 20) & 0xF;
public readonly uint Secondary => (this.Props[7] >> 10) & 0xF;
public readonly uint Campaign => this.Props[7] & 0xF;
public readonly uint Level => (this.Props[7] >> 4) & 0x3F;
public readonly bool IsPvp => ((this.Props[7] >> 9) & 0x1) == 0x1;
}
@@ -1,184 +0,0 @@
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("ControlAction")]
public enum ControlAction : uint
{
None = 0,
Screenshot = 0xAE,
// Panels
CloseAllPanels = 0x85,
ToggleInventoryWindow = 0x8B,
OpenScoreChart = 0xBD,
OpenTemplateManager = 0xD3,
OpenSaveEquipmentTemplate = 0xD4,
OpenSaveSkillTemplate = 0xD5,
OpenParty = 0xBF,
OpenGuild = 0xBA,
OpenFriends = 0xB9,
ToggleAllBags = 0xB8,
OpenMissionMap = 0xB6,
OpenBag2 = 0xB5,
OpenBag1 = 0xB4,
OpenBelt = 0xB3,
OpenBackpack = 0xB2,
OpenSkillsAndAttributes = 0x8F,
OpenQuestLog = 0x8E,
OpenWorldMap = 0x8C,
OpenOptions = 0x8D,
OpenHero = 0x8A,
// Weapon sets
CycleEquipment = 0x86,
ActivateWeaponSet1 = 0x81,
ActivateWeaponSet2,
ActivateWeaponSet3,
ActivateWeaponSet4,
DropItem = 0xCD, // drops bundle item >> flags, ashes, etc
// Chat
CharReply = 0xBE,
OpenChat = 0xA1,
OpenAlliance = 0x88,
ReverseCamera = 0x90,
StrafeLeft = 0x91,
StrafeRight = 0x92,
TurnLeft = 0xA2,
TurnRight = 0xA3,
MoveBackward = 0xAC,
MoveForward = 0xAD,
CancelAction = 0xAF,
Interact = 0x80,
ReverseDirection = 0xB1,
Autorun = 0xB7,
Follow = 0xCC,
// Targeting
TargetPartyMember1 = 0x96,
TargetPartyMember2,
TargetPartyMember3,
TargetPartyMember4,
TargetPartyMember5,
TargetPartyMember6,
TargetPartyMember7,
TargetPartyMember8,
TargetPartyMember9 = 0xC6,
TargetPartyMember10,
TargetPartyMember11,
TargetPartyMember12,
TargetNearestItem = 0xC3,
TargetNextItem = 0xC4,
TargetPreviousItem = 0xC5,
TargetPartyMemberNext = 0xCA,
TargetPartyMemberPrevious = 0xCB,
TargetAllyNearest = 0xBC,
ClearTarget = 0xE3,
TargetSelf = 0xA0, // also 0x96
TargetPriorityTarget = 0x9F,
TargetNearestEnemy = 0x93,
TargetNextEnemy = 0x95,
TargetPreviousEnemy = 0x9E,
ShowOthers = 0x89,
ShowTargets = 0x94,
CameraZoomIn = 0xCE,
CameraZoomOut = 0xCF,
// Party/Hero commands
ClearPartyCommands = 0xDB,
CommandParty = 0xD6,
CommandHero1,
CommandHero2,
CommandHero3,
CommandHero4 = 0x102,
CommandHero5,
CommandHero6,
CommandHero7,
OpenHero1PetCommander = 0xE0,
OpenHero2PetCommander,
OpenHero3PetCommander,
OpenHero4PetCommander = 0xFE,
OpenHero5PetCommander,
OpenHero6PetCommander,
OpenHero7PetCommander,
OpenHeroCommander1 = 0xDC,
OpenHeroCommander2,
OpenHeroCommander3,
OpenPetCommander,
OpenHeroCommander4 = 0x126,
OpenHeroCommander5,
OpenHeroCommander6,
OpenHeroCommander7,
Hero1Skill1 = 0xE5,
Hero1Skill2,
Hero1Skill3,
Hero1Skill4,
Hero1Skill5,
Hero1Skill6,
Hero1Skill7,
Hero1Skill8,
Hero2Skill1,
Hero2Skill2,
Hero2Skill3,
Hero2Skill4,
Hero2Skill5,
Hero2Skill6,
Hero2Skill7,
Hero2Skill8,
Hero3Skill1,
Hero3Skill2,
Hero3Skill3,
Hero3Skill4,
Hero3Skill5,
Hero3Skill6,
Hero3Skill7,
Hero3Skill8,
Hero4Skill1 = 0x106,
Hero4Skill2,
Hero4Skill3,
Hero4Skill4,
Hero4Skill5,
Hero4Skill6,
Hero4Skill7,
Hero4Skill8,
Hero5Skill1,
Hero5Skill2,
Hero5Skill3,
Hero5Skill4,
Hero5Skill5,
Hero5Skill6,
Hero5Skill7,
Hero5Skill8,
Hero6Skill1,
Hero6Skill2,
Hero6Skill3,
Hero6Skill4,
Hero6Skill5,
Hero6Skill6,
Hero6Skill7,
Hero6Skill8,
Hero7Skill1,
Hero7Skill2,
Hero7Skill3,
Hero7Skill4,
Hero7Skill5,
Hero7Skill6,
Hero7Skill7,
Hero7Skill8,
// Skills
UseSkill1 = 0xA4,
UseSkill2,
UseSkill3,
UseSkill4,
UseSkill5,
UseSkill6,
UseSkill7,
UseSkill8
};
@@ -1,20 +0,0 @@
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("District")]
public enum District
{
Current,
International,
American,
EuropeEnglish,
EuropeFrench,
EuropeGerman,
EuropeItalian,
EuropeSpanish,
EuropePolish,
EuropeRussian,
AsiaKorean,
AsiaChinese,
AsiaJapanese,
Unknown = 0xff
}
-90
View File
@@ -1,90 +0,0 @@
using Daybreak.API.Models;
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
// Very incomplete. GWCA contains a much better definition
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1AC)]
[GWCAEquivalent("Frame")]
public readonly struct Frame
{
[FieldOffset(0x0008)]
public readonly uint FrameLayout;
[FieldOffset(0x0018)]
public readonly uint VisibilityFlags;
[FieldOffset(0x0020)]
public readonly uint Type;
[FieldOffset(0x0024)]
public readonly uint TemplateType;
[FieldOffset(0x00A8)]
public readonly GuildWarsArray<FrameInteractionCallback> FrameCallbacks;
[FieldOffset(0x00b8)]
public readonly uint ChildOffsetId;
[FieldOffset(0x00bc)]
public readonly uint FrameId;
[FieldOffset(0x0128)]
public readonly FrameRelation Relation;
[FieldOffset(0x018C)]
public readonly uint FrameState;
public bool IsCreated => (this.FrameState & 0x4) != 0;
public bool IsHidden => (this.FrameState & 0x200) != 0;
public bool IsVisible => !this.IsHidden;
public bool IsDIsabled => (this.FrameState & 0x10) != 0;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1C)]
public readonly unsafe struct FrameRelation
{
[FieldOffset(0x0000)]
public readonly FrameRelation* Parent;
[FieldOffset(0x000C)]
public readonly uint FrameHashId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly unsafe struct InteractionMessage
{
public readonly uint FrameId;
public readonly UIMessage MessageId;
public readonly void** WParam;
}
public unsafe delegate void UIInteractionCallback(InteractionMessage* message, void* wParam, void* lParam);
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
public unsafe readonly struct FrameInteractionCallback
{
public readonly void* Callback; // UIInteractionCallback
public readonly void* UiCtl_Context;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly unsafe struct CharSelectorContext
{
[FieldOffset(0x0000)]
public readonly uint Vtable;
[FieldOffset(0x0004)]
public readonly uint FrameId;
[FieldOffset(0x0008)]
public readonly GuildWarsArray<WrappedPointer<CharSelectorChar>> Chars;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct CharSelectorChar
{
[FieldOffset(0x0020)]
public readonly Array20Char Name;
}
@@ -1,35 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("GameContext")]
public readonly unsafe struct GameContext
{
[FieldOffset(0x0008)]
public readonly AgentGameContext* AgentGameContext;
[FieldOffset(0x0014)]
public readonly MapContext* MapContext;
[FieldOffset(0x0018)]
public readonly TextParserContext* TextParserContext;
[FieldOffset(0x0028)]
public readonly AccountGameContext* AccountContext;
[FieldOffset(0x002C)]
public readonly WorldContext* WorldContext;
[FieldOffset(0x003C)]
public readonly GuildContext* GuildContext;
[FieldOffset(0x0040)]
public readonly ItemContext* ItemContext;
[FieldOffset(0x0044)]
public readonly CharContext* CharContext;
[FieldOffset(0x004C)]
public readonly PartyContext* PartyContext;
}
-12
View File
@@ -1,12 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("GamePos")]
public readonly struct GamePos
{
public readonly float X;
public readonly float Y;
public readonly uint Plane;
}
@@ -1,11 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x78)]
[GWCAEquivalent("GameplayContext")]
public readonly struct GameplayContext
{
[FieldOffset(0x4C)]
public readonly float MissionMapZoom;
}
-111
View File
@@ -1,111 +0,0 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("GuildContext")]
public readonly struct GuildContext
{
[FieldOffset(0x0034)]
public readonly Array20Char PlayerName;
[FieldOffset(0x0060)]
public readonly uint PlayerBuildIndex;
[FieldOffset(0x0078)]
public readonly Array256Char Announcement;
[FieldOffset(0x0287)]
public readonly Array20Char AnnouncementAuthor;
[FieldOffset(0x02A0)]
public readonly uint PlayerGuildRank;
[FieldOffset(0x02A8)]
public readonly GuildWarsArray<TownAlliance> FactionOutpostGuilds;
[FieldOffset(0x02B8)]
public readonly uint KurzickTownCount;
[FieldOffset(0x02BC)]
public readonly uint LuxonTownCount;
[FieldOffset(0x02F8)]
public readonly GuildWarsArray<WrappedPointer<Guild>> Guilds;
[FieldOffset(0x0358)]
public readonly GuildWarsArray<WrappedPointer<GuildPlayer>> PlayerRoster;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct TownAlliance
{
public readonly uint Rank;
public readonly uint Allegiance;
public readonly uint Faction;
public readonly Array32Char Name;
public readonly Array5Char Tag;
public readonly CapeDesign Cape;
public readonly uint MapId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct CapeDesign
{
public readonly uint CapeBgColor;
public readonly uint CapeDetailColor;
public readonly uint CapeEmblemColor;
public readonly uint CapeShape;
public readonly uint CapeDetail;
public readonly uint CapeEmblem;
public readonly uint CapeTrim;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x174)]
public readonly unsafe struct GuildPlayer
{
[FieldOffset(0x0004)]
public readonly char* NamePtr;
[FieldOffset(0x0008)]
public readonly Array20Char InvitedName;
[FieldOffset(0x0030)]
public readonly Array20Char CurrentName;
[FieldOffset(0x0050)]
public readonly Array20Char InviterName;
[FieldOffset(0x0080)]
public readonly uint InviteTime;
[FieldOffset(0x0084)]
public readonly Array20Char PromoterName;
[FieldOffset(0x00DC)]
public readonly uint Offline;
[FieldOffset(0x00E0)]
public readonly uint MemberType;
[FieldOffset(0x00E4)]
public readonly uint Status;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xAC)]
public readonly struct Guild
{
[FieldOffset(0x0024)]
public readonly uint Index;
[FieldOffset(0x0028)]
public readonly uint Rank;
[FieldOffset(0x002C)]
public readonly uint Features;
[FieldOffset(0x0030)]
public readonly Array32Char Name;
[FieldOffset(0x0070)]
public readonly uint Rating;
[FieldOffset(0x0074)]
public readonly uint Faction;
[FieldOffset(0x0078)]
public readonly uint FactionPoints;
[FieldOffset(0x007C)]
public readonly uint QualifierPoints;
[FieldOffset(0x0080)]
public readonly Array8Char Tag;
[FieldOffset(0x0090)]
public readonly CapeDesign Cape;
}
-44
View File
@@ -1,44 +0,0 @@
using System.Extensions;
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x24)]
public readonly struct HeroFlag
{
[FieldOffset(0x0000)]
public readonly uint HeroId;
[FieldOffset(0x0004)]
public readonly uint AgentId;
[FieldOffset(0x0008)]
public readonly uint Level;
[FieldOffset(0x000C)]
public readonly Behavior Behavior;
[FieldOffset(0x0010)]
public readonly Vector2 Flag;
[FieldOffset(0x0020)]
public readonly uint LockedTargetId;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x78)]
[GWCAEquivalent("HeroInfo")]
public readonly struct HeroInfo
{
[FieldOffset(0x0000)]
public readonly uint HeroId;
[FieldOffset(0x0004)]
public readonly uint AgentId;
[FieldOffset(0x0008)]
public readonly uint Level;
[FieldOffset(0x000C)]
public readonly Profession Primary;
[FieldOffset(0x0010)]
public readonly Profession Secondary;
[FieldOffset(0x0014)]
public readonly uint HeroFileId;
[FieldOffset(0x0018)]
public readonly uint ModelFileId;
[FieldOffset(0x0050)]
public readonly Array20Char Name;
}
@@ -1,31 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("InstanceType")]
public enum InstanceType
{
Outpost,
Explorable,
Loading
};
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly unsafe struct InstanceInfoContext
{
[FieldOffset(0x0004)]
public readonly InstanceType InstanceType;
[FieldOffset(0x0008)]
public readonly AreaInfo* CurrentMapInfo;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("AreaInfo")]
public readonly struct AreaInfo
{
public readonly uint Campaign;
public readonly uint Continent;
public readonly uint Region;
public readonly uint RegionType;
public readonly uint Flags;
}
@@ -1,59 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("Inventory")]
public readonly unsafe struct Inventory
{
public readonly Bag* UnusedBag;
public readonly Bag* Backpack;
public readonly Bag* BeltPouch;
public readonly Bag* Bag1;
public readonly Bag* Bag2;
public readonly Bag* EquipmentPack;
public readonly Bag* MaterialStorage;
public readonly Bag* UnclaimedItems;
public readonly Bag* Storage1;
public readonly Bag* Storage2;
public readonly Bag* Storage3;
public readonly Bag* Storage4;
public readonly Bag* Storage5;
public readonly Bag* Storage6;
public readonly Bag* Storage7;
public readonly Bag* Storage8;
public readonly Bag* Storage9;
public readonly Bag* Storage10;
public readonly Bag* Storage11;
public readonly Bag* Storage12;
public readonly Bag* Storage13;
public readonly Bag* Storage14;
public readonly Bag* EquippedItems;
public readonly Item* Bundle;
public readonly uint StoragePanesUnlocked;
public readonly Bag*[] Bags => [
this.Backpack,
this.BeltPouch,
this.Bag1,
this.Bag2,
this.EquipmentPack,
this.MaterialStorage,
this.UnclaimedItems,
this.Storage1,
this.Storage2,
this.Storage3,
this.Storage4,
this.Storage5,
this.Storage6,
this.Storage7,
this.Storage8,
this.Storage9,
this.Storage10,
this.Storage11,
this.Storage12,
this.Storage13,
this.Storage14,
this.EquippedItems
];
}
-129
View File
@@ -1,129 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
public enum ItemType : byte
{
Salvage,
Axe = 2,
Bag,
Boots,
Bow,
Bundle,
Chestpiece,
Rune_Mod,
Usable,
Dye,
Materials_Zcoins,
Offhand,
Gloves,
Hammer = 15,
Headpiece,
CC_Shards,
Key,
Leggings,
Gold_Coin,
Quest_Item,
Wand,
Shield = 24,
Staff = 26,
Sword,
Kit = 29,
Trophy,
Scroll,
Daggers,
Present,
Minipet,
Scythe,
Spear,
Storybook = 43,
Costume,
Costume_Headpiece,
Unknown = 0xff
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("ItemModifier")]
public readonly struct ItemModifier(uint mod)
{
public static readonly ItemModifier Zero = new();
public readonly uint Mod = mod;
public uint Identifier => this.Mod >> 16;
public uint Arg1 => (this.Mod & 0x0000FF00) >> 8;
public uint Arg2 => this.Mod & 0x000000FF;
public uint Arg => this.Mod & 0x0000FFFF;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("Item")]
public readonly unsafe struct Item
{
[FieldOffset(0x0000)]
public readonly uint ItemId;
[FieldOffset(0x0004)]
public readonly uint AgentId;
[FieldOffset(0x0008)]
public readonly Bag* BagEquipped;
[FieldOffset(0x000C)]
public readonly Bag* Bag;
[FieldOffset(0x0010)]
public readonly ItemModifier* Modifiers;
[FieldOffset(0x0014)]
public readonly uint ModifiersCount;
[FieldOffset(0x001C)]
public readonly uint ModelFileId;
[FieldOffset(0x0020)]
public readonly ItemType Type;
[FieldOffset(0x0024)]
public readonly ushort Value;
[FieldOffset(0x0028)]
public readonly uint Interaction;
[FieldOffset(0x002C)]
public readonly uint ModelId;
[FieldOffset(0x0030)]
public readonly char* InfoString;
[FieldOffset(0x0034)]
public readonly char* NameEncoded;
[FieldOffset(0x0038)]
public readonly char* CompleteNameEncoded;
[FieldOffset(0x003C)]
public readonly char* SingleItemName;
[FieldOffset(0x0048)]
public readonly ushort ItemFormula;
[FieldOffset(0x004A)]
public readonly byte IsMaterialSalvageable;
[FieldOffset(0x004C)]
public readonly ushort Quantity;
[FieldOffset(0x004E)]
public readonly byte Equipped;
[FieldOffset(0x004F)]
public readonly byte Profession;
[FieldOffset(0x0050)]
public readonly uint Slot;
public readonly bool Stackable => (this.Interaction & 0x80000) != 0;
public readonly bool Inscribable => (this.Interaction & 0x08000000) != 0;
}
@@ -1,17 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("ItemContext")]
public readonly struct ItemContext
{
[FieldOffset(0x0024)]
public readonly GuildWarsArray<WrappedPointer<Bag>> Bags;
[FieldOffset(0x00B8)]
public readonly GuildWarsArray<WrappedPointer<Item>> Items;
[FieldOffset(0x00F8)]
public readonly WrappedPointer<Inventory> Inventory;
}
@@ -1,18 +0,0 @@
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("Language")]
public enum Language
{
English,
Korean,
French,
German,
Italian,
Spanish,
TraditionalChinese,
Japanese = 8,
Polish,
Russian,
BorkBorkBork = 17,
Unknown = 0xff
}
@@ -1,9 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("MapContext")]
public readonly struct MapContext
{
}
-63
View File
@@ -1,63 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit)]
[GWCAEquivalent("NPC")]
public readonly unsafe struct NpcContext
{
[FieldOffset(0x0000)]
public readonly uint ModelId;
[FieldOffset(0x000C)]
public readonly uint Sex;
[FieldOffset(0x0010)]
public readonly NpcFlags Flags;
[FieldOffset(0x0014)]
public readonly uint Primary;
[FieldOffset(0x001C)]
public readonly byte DefaultLevel;
[FieldOffset(0x0020)]
public readonly char* EncName;
[FieldOffset(0x0028)]
public readonly int FilesCount;
[FieldOffset(0x002C)]
public readonly int FilesCapacity;
[Flags]
public enum NpcFlags : uint
{
None = 0x0000,
Henchman = 0x0010,
Hero = 0x0020,
Spirit = 0x4000,
Minion = 0x0100,
Pet = 0x000D
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("PetInfo")]
public readonly unsafe struct PetContext
{
public readonly uint AgentId;
public readonly uint OwnerAgentId;
public readonly char* PetName;
public readonly uint ModelFileId1;
public readonly uint ModelFileId2;
public readonly Behavior Behavior;
public readonly uint LockedTargetId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct ControlledMinion
{
public readonly uint AgentId;
public readonly uint MinionCount;
}
-127
View File
@@ -1,127 +0,0 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum PlayerPartyMemberState : uint
{
None = 0,
Connected = 1,
Ticked = 2
}
public enum PartySearchType
{
PartySearchType_Hunting = 0,
PartySearchType_Mission = 1,
PartySearchType_Quest = 2,
PartySearchType_Trade = 3,
PartySearchType_Guild = 4,
};
[Flags]
public enum PartyFlags : uint
{
None = 0,
HardMode = 0x10, // Bit 4
Defeated = 0x20, // Bit 5
PartyLeader = 0x80 // Bit 7 (0x7th position)
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xC)]
public readonly unsafe struct PartyMoraleContext
{
[FieldOffset(0x000C)]
public readonly PartyMemberMoraleInfo* MoraleInfo;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct PartyMemberMoraleInfo
{
[FieldOffset(0x0000)]
public readonly uint AgentId;
[FieldOffset(0x0014)]
public readonly uint Morale;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x58)]
[GWCAEquivalent("PartyContext")]
public readonly unsafe struct PartyContext
{
[FieldOffset(0x0014)]
public readonly PartyFlags Flags;
[FieldOffset(0x0040)]
public readonly GuildWarsArray<WrappedPointer<PartyInfo>> Parties;
[FieldOffset(0x0054)]
public readonly PartyInfo* PlayerParty;
[FieldOffset(0x00C0)]
public readonly GuildWarsArray<WrappedPointer<PartySearch>> PartySearches;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
public readonly struct PlayerPartyMember
{
public readonly uint LogingNumber;
public readonly uint CalledTargetId;
public readonly PlayerPartyMemberState State;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x18)]
public readonly struct HeroPartyMember
{
[FieldOffset(0x0000)]
public readonly uint AgentId;
[FieldOffset(0x0004)]
public readonly uint OwnerPlayerId;
[FieldOffset(0x0008)]
public readonly uint HeroId;
[FieldOffset(0x0014)]
public readonly uint Level;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x34)]
public readonly struct HenchmanPartyMember
{
[FieldOffset(0x0000)]
public readonly uint AgentId;
[FieldOffset(0x002C)]
public readonly Profession Profession;
[FieldOffset(0x0030)]
public readonly uint Level;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x84)]
[GWCAEquivalent("PartyInfo")]
public readonly struct PartyInfo
{
[FieldOffset(0x0000)]
public readonly uint PartyId;
[FieldOffset(0x0004)]
public readonly GuildWarsArray<PlayerPartyMember> Players;
[FieldOffset(0x0014)]
public readonly GuildWarsArray<HenchmanPartyMember> Henchmen;
[FieldOffset(0x0024)]
public readonly GuildWarsArray<HeroPartyMember> Heroes;
[FieldOffset(0x0034)]
public readonly GuildWarsArray<uint> Others;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("PartySearch")]
public readonly struct PartySearch
{
public readonly uint PartySearchId;
public readonly PartySearchType PartySearchType;
public readonly uint HardMode;
public readonly uint District;
public readonly uint Language;
public readonly uint PartySize;
public readonly uint HeroCount;
public readonly Array32Char Message;
public readonly Array20Char PartyLeader;
public readonly Profession Primary;
public readonly Profession Secondary;
public readonly uint Level;
public readonly uint Timestamp;
}
@@ -1,20 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x34)]
public unsafe struct PartySearchWindowContext
{
[FieldOffset(0x0000)]
public uint* Vtable;
[FieldOffset(0x0004)]
public uint FrameId;
[FieldOffset(0x0020)]
public uint SelectedPartySearchId;
[FieldOffset(0x0024)]
public uint SelectedHeroId;
[FieldOffset(0x0028)]
public uint SelectedHenchmanId;
[FieldOffset(0x002C)]
public uint CurrentTab;
}
@@ -1,48 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum PlayerContextFlags : uint
{
None = 0,
PvP = 0x800
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x50)]
[GWCAEquivalent("PlayerContext")]
public readonly unsafe struct PlayerContext
{
[FieldOffset(0x0000)]
public readonly int AgentId;
[FieldOffset(0x0014)]
public readonly PlayerContextFlags Flags;
[FieldOffset(0x0018)]
public readonly uint PrimaryProfession;
[FieldOffset(0x001C)]
public readonly uint SecondaryProfession;
[FieldOffset(0x0024)]
public readonly char* NameEncoded;
[FieldOffset(0x0028)]
public readonly char* Name;
[FieldOffset(0x002C)]
public readonly uint PartyLeaderPlayerNumber;
[FieldOffset(0x0030)]
public readonly uint ActiveTitleTier;
[FieldOffset(0x0034)]
public readonly uint ReforgedOrDhuumsFlags;
[FieldOffset(0x0038)]
public readonly uint PlayerNumber;
[FieldOffset(0x003C)]
public readonly uint PartySize;
}
@@ -1,10 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct PlayerControlledCharContext
{
[FieldOffset(0x0014)]
public readonly uint AgentId;
}
@@ -1,25 +0,0 @@
using System.Extensions;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("PreGameContext")]
public readonly struct PreGameContext
{
[FieldOffset(0x0000)]
public readonly uint FrameId;
[FieldOffset(0x0124)]
public readonly uint ChosenCharacterIndex;
[FieldOffset(0x0148)]
public readonly GuildWarsArray<LoginCharacterContext> LoginCharacters;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public readonly struct LoginCharacterContext
{
[FieldOffset(0x0004)]
public readonly Array20Char CharacterName;
}
@@ -1,45 +0,0 @@
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("EnumPreference")]
public enum EnumPreference : uint
{
CharSortOrder = 0,
AntiAliasing = 1,
Reflections = 2,
ShaderQuality = 3,
ShadowQuality = 4,
TerrainQuality = 5,
InterfaceSize = 6,
FrameLimiter = 7,
Count = 8
}
[GWCAEquivalent("CharSortOrder")]
public enum CharSortOrder : uint
{
None = 0,
Alphabetize = 1,
PvPRP = 2
}
[GWCAEquivalent("NumberPreference")]
public enum NumberPreference : uint
{
AutoTournPartySort = 0,
ChatState = 1,
Count = 0x2b
}
[GWCAEquivalent("FlagPreference")]
public enum FlagPreference : uint
{
}
[GWCAEquivalent("StringPreference")]
public enum StringPreference : uint
{
Unk1 = 0,
Unk2 = 1,
LastCharacterName = 2,
Count = 3
}
@@ -1,48 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("Profession")]
public enum Profession : uint
{
None,
Warrior,
Ranger,
Monk,
Necromancer,
Mesmer,
Elementalist,
Assassin,
Ritualist,
Paragon,
Dervish
}
public enum ProfessionByte : byte
{
None,
Warrior,
Ranger,
Monk,
Necromancer,
Mesmer,
Elementalist,
Assassin,
Ritualist,
Paragon,
Dervish
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x0014)]
public readonly struct ProfessionsContext
{
public readonly uint AgentId;
public readonly Profession CurrentPrimary;
public readonly Profession CurrentSecondary;
public readonly uint UnlockedProfessionsFlags;
public bool ProfessionUnlocked(int professionId)
{
return (this.UnlockedProfessionsFlags & (1U << professionId)) != 0;
}
}
-26
View File
@@ -1,26 +0,0 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x0034)]
[GWCAEquivalent("Quest")]
public readonly struct QuestContext
{
[FieldOffset(0x0000)]
public readonly uint QuestId;
[FieldOffset(0x0014)]
public readonly uint MapFrom;
[FieldOffset(0x0018)]
public readonly Vector3 Marker;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
public readonly unsafe struct MissionObjectiveContext
{
public readonly uint ObjectiveId;
public readonly char* EncodedString;
public readonly uint Type;
}
@@ -1,13 +0,0 @@
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("ServerRegion")]
public enum ServerRegion
{
International = -2,
America = 0,
Korea,
Europe,
China,
Japan,
Unknown = 0xff
}
-62
View File
@@ -1,62 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("SkillbarSkill")]
public readonly struct SkillContext
{
public readonly uint Adrenaline1;
public readonly uint Adrenaline2;
public readonly uint Recharge;
public readonly uint Id;
public readonly uint Event;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xBC)]
[GWCAEquivalent("Skillbar")]
public readonly struct SkillbarContext
{
public readonly uint AgentId;
public readonly SkillContext Skill0;
public readonly SkillContext Skill1;
public readonly SkillContext Skill2;
public readonly SkillContext Skill3;
public readonly SkillContext Skill4;
public readonly SkillContext Skill5;
public readonly SkillContext Skill6;
public readonly SkillContext Skill7;
public readonly uint Disabled;
public readonly uint Casting;
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x10)]
[GWCAEquivalent("Buff")]
public readonly struct Buff
{
[FieldOffset(0x0000)]
public readonly uint SkillId;
[FieldOffset(0x0008)]
public readonly uint BuffId;
[FieldOffset(0x000C)]
public readonly uint TargetAgentId;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x18)]
[GWCAEquivalent("Effect")]
public readonly struct Effect
{
public readonly uint SkillId;
public readonly uint AttributeLevel;
public readonly uint EffectId;
public readonly uint AgentId;
public readonly float Duration;
public readonly uint Timestamp;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct DupeSkill
{
public readonly uint SkillId;
public readonly uint Count;
}
@@ -1,14 +0,0 @@
using System.Extensions;
namespace Daybreak.API.Interop.GuildWars;
[GWCAEquivalent("SkillTemplate")]
public readonly struct SkillTemplate(uint primary, uint secondary, uint attributeCount, Array12Uint attributeIds, Array12Uint attributeValues, Array8Uint skills)
{
public readonly uint Primary = primary;
public readonly uint Secondary = secondary;
public readonly uint AttributesCount = attributeCount;
public readonly Array12Uint AttributeIds = attributeIds;
public readonly Array12Uint AttributeValues = attributeValues;
public readonly Array8Uint Skills = skills;
}
-13
View File
@@ -1,13 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("TagInfo")]
public readonly struct TagInfo
{
public readonly ushort GuildId;
public readonly byte Primary;
public readonly byte Secondary;
public readonly ushort Level;
}
@@ -1,11 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("TextParser")]
public struct TextParserContext
{
[FieldOffset(0x01D0)]
public Language Language;
}
-51
View File
@@ -1,51 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[Flags]
public enum TitleProps : uint
{
None = 0,
PercentageBased = 1,
HasTiers = 2
// Note: The HasTiers check uses (props & 3) == 2, which means bit 1 is set but bit 0 is not
}
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x2C)]
[GWCAEquivalent("Title")]
public readonly struct TitleContext
{
[FieldOffset(0x0000)]
public readonly TitleProps Props;
[FieldOffset(0x0004)]
public readonly uint CurrentPoints;
[FieldOffset(0x0008)]
public readonly uint CurrentTitleTierIndex;
[FieldOffset(0x000C)]
public readonly uint PointsNeededForCurrentRank;
[FieldOffset(0x0014)]
public readonly uint NextTitleTierIndex;
[FieldOffset(0x0018)]
public readonly uint PointsNeededForNextRank;
[FieldOffset(0x001C)]
public readonly uint MaxTitleRank;
[FieldOffset(0x0020)]
public readonly uint MaxTitleTierIndex;
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
[GWCAEquivalent("TitleTier")]
public readonly unsafe struct TitleTier
{
public readonly TitleProps Props;
public readonly uint TierNumber;
public readonly char* TierNameEncoded;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[GWCAEquivalent("TitleClientData")]
public readonly struct TitleClientData
{
public readonly uint TitleId;
public readonly uint NameId;
}
@@ -1,174 +0,0 @@
using System.Numerics;
using System.Runtime.InteropServices;
namespace Daybreak.API.Interop.GuildWars;
[StructLayout(LayoutKind.Explicit, Pack = 1)]
[GWCAEquivalent("WorldContext")]
public readonly unsafe struct WorldContext
{
[FieldOffset(0x0000)]
public readonly AccountInfoContext* AccountInfo;
[FieldOffset(0x0024)]
public readonly GuildWarsArray<uint> MerchItems;
[FieldOffset(0x0034)]
public readonly GuildWarsArray<uint> MerchItems2;
[FieldOffset(0x007C)]
public readonly GuildWarsArray<MapAgentContext> MapAgents;
[FieldOffset(0x009C)]
public readonly Vector3 AllFlag;
[FieldOffset(0x00AC)]
public readonly GuildWarsArray<PartyAttribute> Attributes;
[FieldOffset(0x0508)]
public readonly GuildWarsArray<AgentEffects> PartyEffects;
[FieldOffset(0x0528)]
public readonly uint ActiveQuestId;
[FieldOffset(0x052C)]
public readonly GuildWarsArray<QuestContext> QuestLog;
[FieldOffset(0x0564)]
public readonly GuildWarsArray<MissionObjectiveContext> MissionObjectives;
[FieldOffset(0x0574)]
public readonly GuildWarsArray<uint> HenchmenAgentIds;
[FieldOffset(0x0584)]
public readonly GuildWarsArray<HeroFlag> HeroFlags;
[FieldOffset(0x0594)]
public readonly GuildWarsArray<HeroInfo> HeroInfos;
[FieldOffset(0x05A4)]
public readonly GuildWarsArray<nuint> CartographedAreas;
[FieldOffset(0x05BC)]
public readonly GuildWarsArray<ControlledMinion> ControlledMinions;
[FieldOffset(0x05CC)]
public readonly GuildWarsArray<uint> MissionsCompleted;
[FieldOffset(0x05DC)]
public readonly GuildWarsArray<uint> MissionsBonus;
[FieldOffset(0x05EC)]
public readonly GuildWarsArray<uint> MissionsCompletedHardMode;
[FieldOffset(0x05FC)]
public readonly GuildWarsArray<uint> MissionsBonusHardMode;
[FieldOffset(0x060C)]
public readonly GuildWarsArray<uint> UnlockedMap;
[FieldOffset(0x062C)]
public readonly GuildWarsArray<PartyMoraleContext> PartyMorale;
[FieldOffset(0x067C)]
public readonly uint PlayerNumber;
[FieldOffset(0x0680)]
public readonly PlayerControlledCharContext* PlayerControlledChar;
[FieldOffset(0x0684)]
public readonly uint HardModeUnlocked;
[FieldOffset(0x06AC)]
public readonly GuildWarsArray<PetContext> Pets;
[FieldOffset(0x06BC)]
public readonly GuildWarsArray<ProfessionsContext> Professions;
[FieldOffset(0x06F0)]
public readonly GuildWarsArray<SkillbarContext> Skillbars;
[FieldOffset(0x0700)]
public readonly GuildWarsArray<uint> LearnableCharacterSkills;
[FieldOffset(0x0710)]
public readonly GuildWarsArray<uint> UnlockedCharacterSkills;
[FieldOffset(0x0720)]
public readonly GuildWarsArray<DupeSkill> DupeSkills;
[FieldOffset(0x0740)]
public readonly uint Experience;
[FieldOffset(0x0748)]
public readonly uint CurrentKurzick;
[FieldOffset(0x0750)]
public readonly uint TotalKurzick;
[FieldOffset(0x0758)]
public readonly uint CurrentLuxon;
[FieldOffset(0x0760)]
public readonly uint TotalLuxon;
[FieldOffset(0x0768)]
public readonly uint CurrentImperial;
[FieldOffset(0x0770)]
public readonly uint TotalImperial;
[FieldOffset(0x0788)]
public readonly uint Level;
[FieldOffset(0x0790)]
public readonly uint Morale;
[FieldOffset(0x0798)]
public readonly uint CurrentBalthazar;
[FieldOffset(0x07A0)]
public readonly uint TotalBalthazar;
[FieldOffset(0x07A8)]
public readonly uint CurrentSkillPoints;
[FieldOffset(0x07B0)]
public readonly uint TotalSkillPoints;
[FieldOffset(0x07B8)]
public readonly uint MaxKurzick;
[FieldOffset(0x07BC)]
public readonly uint MaxLuxon;
[FieldOffset(0x07C0)]
public readonly uint MaxBalthazar;
[FieldOffset(0x07C4)]
public readonly uint MaxImperial;
[FieldOffset(0x07CC)]
public readonly GuildWarsArray<AgentInfo> AgentInfos;
[FieldOffset(0x07FC)]
public readonly GuildWarsArray<NpcContext> Npcs;
[FieldOffset(0x080C)]
public readonly GuildWarsArray<PlayerContext> Players;
[FieldOffset(0x081C)]
public readonly GuildWarsArray<TitleContext> Titles;
[FieldOffset(0x082C)]
public readonly GuildWarsArray<TitleTier> TitleTiers;
[FieldOffset(0x083C)]
public readonly GuildWarsArray<uint> VanquishedAreas;
[FieldOffset(0x084C)]
public readonly uint FoesKilled;
[FieldOffset(0x0850)]
public readonly uint FoesToKill;
}
-9
View File
@@ -1,9 +0,0 @@
namespace Daybreak.API.Interop;
public interface IHook<T>
: IDisposable where T : Delegate
{
public T Continue { get; }
public nuint ContinueAddress { get; }
}
-78
View File
@@ -1,78 +0,0 @@
using Daybreak.API.Converters;
using System.Text.Json.Serialization;
namespace Daybreak.API.Interop;
[JsonConverter(typeof(PointerValueConverter))]
public readonly struct PointerValue(nuint address) : IEquatable<PointerValue>, IEquatable<nuint>, IEquatable<nint>
{
public readonly nuint Address = address;
public bool Equals(PointerValue other)
{
return this.Address.Equals(other.Address);
}
public bool Equals(nuint other)
{
return this.Address.Equals(other);
}
public bool Equals(nint other)
{
return this.Address.Equals((nuint)other);
}
public override string ToString()
{
return $"0x{this.Address:X8}";
}
public override bool Equals(object? obj) => obj is PointerValue value && this.Equals(value);
public override int GetHashCode()
{
return HashCode.Combine(this.Address);
}
public static bool operator ==(PointerValue left, PointerValue right)
{
return left.Equals(right);
}
public static bool operator !=(PointerValue left, PointerValue right)
{
return !(left == right);
}
public static implicit operator PointerValue(nuint address)
{
return new PointerValue(address);
}
public static implicit operator PointerValue(nint address)
{
return new PointerValue((nuint)address);
}
public static implicit operator PointerValue(int address)
{
return new PointerValue((nuint)address);
}
public static implicit operator PointerValue(uint address)
{
return new PointerValue(address);
}
public static PointerValue Parse(string s)
{
if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)
&& nuint.TryParse(s.AsSpan(2), System.Globalization.NumberStyles.HexNumber, null, out var v))
{
return new PointerValue(v);
}
throw new FormatException($"Invalid pointer format: {s}");
}
}
-9
View File
@@ -1,9 +0,0 @@
using Daybreak.API.Interop;
namespace Daybreak.API.Models;
public sealed class AddressState
{
public required string Name { get; init; }
public required PointerValue Address { get; init; }
}
-38
View File
@@ -1,38 +0,0 @@
using System.Core.Extensions;
using System.Extensions;
namespace Daybreak.API.Models;
public sealed class AsyncRefreshCache<T>(TimeSpan refreshRate, Func<CancellationToken, ValueTask<T>> refreshFunc)
{
private readonly Func<CancellationToken, ValueTask<T>> refreshFunc = refreshFunc.ThrowIfNull();
private readonly SemaphoreSlim semaphoreSlim = new(1);
private T? cache;
private DateTime lastUpdateDateTime = DateTime.MinValue;
private TimeSpan refreshRate = refreshRate;
public async ValueTask<T> GetAsync(CancellationToken cancellationToken)
{
if (this.cache is not null && DateTime.UtcNow - this.lastUpdateDateTime < this.refreshRate)
{
return this.cache;
}
using var ctx = await this.semaphoreSlim.Acquire(cancellationToken);
if (DateTime.UtcNow - this.lastUpdateDateTime > this.refreshRate ||
this.cache is null)
{
this.lastUpdateDateTime = DateTime.UtcNow;
this.cache = await this.refreshFunc(cancellationToken);
}
return this.cache;
}
public void UpdateRefreshRate(TimeSpan newRefreshRate)
{
this.refreshRate = newRefreshRate;
}
}
-27
View File
@@ -1,27 +0,0 @@
using Daybreak.API.Interop;
namespace Daybreak.API.Models;
[GWCAEquivalent("Channel")]
public enum Channel : uint
{
Alliance = 0,
Allies = 1, // coop with two groups for instance.
GWCA1 = 2,
All = 3,
GWCA2 = 4,
Moderator = 5,
Emote = 6,
Warning = 7, // shows in the middle of the screen and does not parse <c> tags
GWCA3 = 8,
Guild = 9,
Global = 10,
Group = 11,
Trade = 12,
Advisory = 13,
Whisper = 14,
Count,
// non-standard channel, but useful.
Command
};
-12
View File
@@ -1,12 +0,0 @@
using Daybreak.API.Interop;
namespace Daybreak.API.Models;
public sealed class HookState
{
public required bool Hooked { get; init; }
public required string Name { get; init; }
public required PointerValue TargetAddress { get; init; }
public required PointerValue ContinueAddress { get; init; }
public required PointerValue DetourAddress { get; init; }
}
-21
View File
@@ -1,21 +0,0 @@
using Daybreak.API.Interop;
namespace Daybreak.API.Models;
[GWCAEquivalent("UIMessage")]
public enum UIMessage : uint
{
None = 0x0,
InitFrame = 0x9,
DestroyFrame = 0xb,
KeyDown = 0x20, // wparam = UIPacket::kKeyAction*
KeyUp = 0x22, // wparam = UIPacket::kKeyAction*
MouseClick = 0x24, // wparam = UIPacket::kMouseClick*
MouseClick2 = 0x31, // wparam = UIPacket::kMouseAction*
MouseAction = 0x32, // wparam = UIPacket::kMouseAction*
FrameMessage_QuerySelectedIndex = 0x59, // Used to query selected index in character selector
WriteToChatLog = 0x10000000 | 0x7F, // wparam = UIPacket::kWriteToChatLog*. Triggered by the game when it wants to add a new message to chat.
WriteToChatLogWithSender = 0x10000000 | 0x80, // wparam = UIPacket::kWriteToChatLogWithSender*. Triggered by the game when it wants to add a new message to chat.
CheckUIState = 0x10000000 | 0x118, // lparam = uint32_t* out state (2 = char select ready)
Logout = 0x10000000 | 0x9D, // wparam = { bool unknown, bool character_select }
}
-66
View File
@@ -1,66 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.API.Models;
public static class UIPackets
{
public enum MouseButtons
{
Left = 0x0,
Middle = 0x1,
Right = 0x2
}
public enum ActionState
{
MouseDown = 0x6,
MouseUp = 0x7,
MouseClick = 0x8,
MouseDoubleClick = 0x9,
DragRelease = 0xb,
KeyDown = 0xe
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct LogChatMessage(string message, Channel channel)
{
[MarshalAs(UnmanagedType.LPWStr)]
public readonly string Message = message;
public readonly Channel Channel = channel;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct UIChatMessage(Channel channel, string message, Channel channel2)
{
public readonly Channel Channel = channel;
[MarshalAs(UnmanagedType.LPWStr)]
public readonly string Message = message;
public readonly Channel Channel2 = channel2;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct KeyAction(uint key, uint wParam = 0x4000, uint lParam = 0x0006)
{
public readonly uint Key = key;
public readonly uint WParam = wParam;
public readonly uint LParam = lParam;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct MouseClick(MouseButtons mouseButton, uint isDoubleClick, uint unknownTypeScreenPos)
{
public readonly MouseButtons MouseButton = mouseButton;
public readonly uint IsDoubleClick = isDoubleClick;
public readonly uint UnknownTypeScreenPos = unknownTypeScreenPos;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct MouseAction(uint frameId, uint childOffsetId, ActionState currentState, nuint wparam = 0, nuint lparam = 0)
{
public readonly uint FrameId = frameId;
public readonly uint ChildOffsetId = childOffsetId;
public readonly ActionState CurrentState = currentState;
public readonly nuint WParam = wparam;
public readonly nuint LParam = lparam;
}
}
@@ -17,10 +17,6 @@ namespace Daybreak.API.Serialization;
[JsonSerializable(typeof(JsonElement))]
[JsonSerializable(typeof(HealthCheckResponse))]
[JsonSerializable(typeof(HealthCheckEntryResponse))]
[JsonSerializable(typeof(HookState))]
[JsonSerializable(typeof(ArraySegment<HookState>))]
[JsonSerializable(typeof(AddressState))]
[JsonSerializable(typeof(ArraySegment<AddressState>))]
[JsonSerializable(typeof(MainPlayerState))]
[JsonSerializable(typeof(QuestLogInformation))]
[JsonSerializable(typeof(QuestInformation))]
+48 -32
View File
@@ -1,8 +1,11 @@
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Extensions;
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using Daybreak.API.Services.Interop;
using Daybreak.Shared.Models.Api;
using System.Core.Extensions;
using System.Extensions;
using System.Extensions.Core;
using System.Runtime.InteropServices;
using ZLinq;
@@ -18,13 +21,6 @@ public sealed class CharacterSelectService(
PreferencesService preferencesService,
ILogger<CharacterSelectService> logger)
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private readonly struct LogOutMessage(uint unknown, uint characterSelect)
{
public readonly uint Unknown = unknown;
public readonly uint CharacterSelect = characterSelect;
}
private readonly InstanceContextService instanceContextService = instanceContextService.ThrowIfNull();
private readonly PlatformContextService platformContextService = platformContextService.ThrowIfNull();
private readonly UIContextService uiContextService = uiContextService.ThrowIfNull();
@@ -42,7 +38,7 @@ public sealed class CharacterSelectService(
{
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null)
gameContext.Pointer->World is null)
{
scopedLogger.LogError("Game context is not initialized");
return default;
@@ -55,21 +51,20 @@ public sealed class CharacterSelectService(
return default;
}
var currentUuid = gameContext.Pointer->CharContext->PlayerUuid.ToString();
var currentUuid = (*(Uuid*)gameContext.Pointer->Character->PlayerUuid).ToString();
var availableChars = new List<CharacterSelectEntry>((int)availableCharsContext.Pointer->Size);
foreach (var charContext in *availableCharsContext.Pointer)
{
var nameSpan = charContext.Name.AsSpan();
var name = new string(nameSpan[..nameSpan.IndexOf('\0')]);
var name = new string(charContext.Name);
availableChars.Add(new CharacterSelectEntry(
charContext.Uuid.ToString(),
(*(Uuid*)charContext.Uuid).ToString(),
name,
charContext.Primary,
charContext.Secondary,
charContext.Campaign,
charContext.MapId,
charContext.Level,
charContext.IsPvp));
(uint)charContext.GetPrimaryProfession(),
(uint)charContext.GetSecondaryProfession(),
(uint)charContext.GetCampaign(),
(uint)charContext.GetMapId(),
(uint)charContext.GetLevel(),
charContext.IsPvP()));
}
// TODO: Reforged sometimes returns Zero UUID even when in-game, need to investigate why
@@ -143,6 +138,26 @@ public sealed class CharacterSelectService(
public uint Play;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
private readonly unsafe struct CharSelectorContext
{
[FieldOffset(0x0000)]
public readonly uint Vtable;
[FieldOffset(0x0004)]
public readonly uint FrameId;
[FieldOffset(0x0008)]
public readonly GuildWarsArray<WrappedPointer<CharSelectorChar>> Chars;
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
private readonly struct CharSelectorChar
{
[FieldOffset(0x0020)]
public readonly Array20Char Name;
}
private async Task<bool> SelectCharacterToPlay(string characterName, bool play, CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
@@ -173,7 +188,7 @@ public sealed class CharacterSelectService(
// Get current selected index
uint selectedIdx = 0;
this.uiContextService.SendFrameUIMessage(panesFrame, UIMessage.FrameMessage_QuerySelectedIndex, null, &selectedIdx);
this.uiContextService.SendFrameUIMessage(panesFrame, UIMessage.kFrameMessage_0x4a, null, &selectedIdx);
// Find target character index
uint targetIdx = 0xFFFF;
@@ -234,19 +249,21 @@ public sealed class CharacterSelectService(
Play = 0
};
var action = new UIPackets.MouseAction(
selectorFrame->FrameId,
selectorFrame->ChildOffsetId,
UIPackets.ActionState.MouseClick,
(nuint)(&buttonParam));
var action = new kMouseAction
{
FrameId = selectorFrame->FrameId,
ChildOffsetId = selectorFrame->ChildOffsetId,
CurrentState = GWCA.GW.UI.UIPacket.ActionState.MouseClick,
Wparam = (nint)(&buttonParam)
};
if (!this.uiContextService.SendFrameUIMessage(parentFrame, UIMessage.MouseClick2, &action))
if (!this.uiContextService.SendFrameUIMessage(parentFrame, UIMessage.kMouseClick2, &action))
{
return false;
}
uint newSelectedIdx = 0;
this.uiContextService.SendFrameUIMessage(panesFrame, UIMessage.FrameMessage_QuerySelectedIndex, null, &newSelectedIdx);
this.uiContextService.SendFrameUIMessage(panesFrame, UIMessage.kFrameMessage_0x4a, null, &newSelectedIdx);
return newSelectedIdx == idx;
}
}
@@ -380,13 +397,12 @@ public sealed class CharacterSelectService(
foreach (var charContext in *availableCharsContext.Pointer)
{
if (charContext.Uuid.ToString() != uuid)
if ((*(Uuid*)charContext.Uuid).ToString() != uuid)
{
continue;
}
var nameSpan = charContext.Name.AsSpan();
var name = new string(nameSpan[..nameSpan.IndexOf('\0')]);
var name = new string(charContext.Name);
return name;
}
@@ -399,10 +415,10 @@ public sealed class CharacterSelectService(
{
await this.gameThreadService.QueueOnGameThread(() =>
{
var logoutMessage = new LogOutMessage(0, 1); // Changed to 1 for character select
var logoutMessage = new kLogout { Unknown = 0, CharacterSelect = 1 };
unsafe
{
this.uiContextService.SendMessage(UIMessage.Logout, (uint)&logoutMessage, 0);
this.uiContextService.SendMessage(UIMessage.kLogout, (uint)&logoutMessage, 0);
}
}, cancellationToken);
}
+9 -3
View File
@@ -1,6 +1,5 @@
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using Daybreak.API.Services.Interop;
using System.Core.Extensions;
using System.Extensions;
@@ -70,8 +69,15 @@ public sealed class ChatService(
span[^2] = '\u0001';
});
using var packet = new UnmanagedStruct<UIPackets.UIChatMessage>(new UIPackets.UIChatMessage(channel, encoded, channel));
this.uiContextService.SendMessage(UIMessage.WriteToChatLog, (nuint)packet.Address, 0x0);
unsafe
{
fixed (char* encodedPtr = encoded)
{
using var packet = new UnmanagedStruct<kPrintChatMessage>(new kPrintChatMessage { Channel = (GWCA.GW.Chat.Channel)channel, Message = (nint)encodedPtr });
this.uiContextService.SendMessage(UIMessage.kWriteToChatLog, (nuint)packet.Address, 0x0);
}
}
}, cancellationToken);
}
}
@@ -5,15 +5,7 @@ namespace Daybreak.API.Services.Interop;
public unsafe sealed class AgentContextService
{
public WrappedPointer<GuildWarsArray<WrappedPointer<AgentContext>>> GetAgentArray()
{
// GuildWarsArray<nint> and GuildWarsArray<WrappedPointer<AgentContext>> have the same memory layout
// since both nint and WrappedPointer<T> are pointer-sized
return (GuildWarsArray<WrappedPointer<AgentContext>>*)GWCA.GW.Agents.GetAgentArray();
}
public uint GetPlayerAgentId() => GWCA.GW.PlayerMgr.GetPlayerAgentId(0);
public uint GetPlayerAgentId()
{
return GWCA.GW.PlayerMgr.GetPlayerAgentId(0);
}
public Agent* GetAgentById(uint agentId) => GWCA.GW.Agents.GetAgentByID(agentId);
}
@@ -10,7 +10,7 @@ public unsafe sealed class GameContextService
public WrappedPointer<PreGameContext> GetPreGameContext() => GWCA.GW.GetPreGameContext();
public WrappedPointer<GuildWarsArray<CharInfoContext>> GetAvailableChars() => GWCA.GW.GetAvailableChars();
public WrappedPointer<GuildWarsArray<CharacterInformation>> GetAvailableChars() => GWCA.GW.GetAvailableChars();
public bool IsMapLoaded() => GWCA.GW.Map.GetIsMapLoaded();
}
@@ -7,7 +7,7 @@ public sealed unsafe class InstanceContextService
{
public WrappedPointer<AreaInfo> GetAreaInfo() => GWCA.GW.Map.GetMapInfo(0);
public ServerRegion GetServerRegion() => GWCA.GW.Map.GetRegion();
public ServerRegion GetServerRegion() => (Daybreak.API.Interop.GuildWars.ServerRegion)GWCA.GW.Map.GetRegion();
public InstanceType GetInstanceType() => GWCA.GW.Map.GetInstanceType();
public InstanceType GetInstanceType() => (Daybreak.API.Interop.GuildWars.InstanceType)GWCA.GW.Map.GetInstanceType();
}
@@ -1,12 +1,13 @@
using Daybreak.API.Interop;
using static Daybreak.API.Interop.GWCA.GW.Constants;
namespace Daybreak.API.Services.Interop;
public sealed class PartyContextService
{
public bool AddHero(uint heroId) => GWCA.GW.PartyMgr.AddHero((int)heroId);
public bool AddHero(uint heroId) => GWCA.GW.PartyMgr.AddHero((HeroID)heroId);
public bool KickHero(uint heroId) => GWCA.GW.PartyMgr.KickHero((int)heroId);
public bool KickHero(uint heroId) => GWCA.GW.PartyMgr.KickHero((HeroID)heroId);
public bool KickAllHeroes() => GWCA.GW.PartyMgr.KickAllHeroes();
@@ -5,7 +5,7 @@ namespace Daybreak.API.Services.Interop;
public sealed class PreferencesService()
{
public uint? GetEnumPreference(EnumPreference pref) => GWCA.GW.UI.GetPreference(pref);
public uint? GetEnumPreference(EnumPreference pref) => GWCA.GW.UI.GetPreference((GWCA.GW.UI.EnumPreference)pref);
public bool SetEnumPreference(EnumPreference pref, uint value) => GWCA.GW.UI.SetPreference(pref, value);
public bool SetEnumPreference(EnumPreference pref, uint value) => GWCA.GW.UI.SetPreference((GWCA.GW.UI.EnumPreference)pref, value);
}
@@ -1,6 +1,5 @@
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using System.Collections.Concurrent;
using System.Core.Extensions;
using System.Extensions.Core;
@@ -23,21 +22,7 @@ public sealed class UIContextService(ILogger<UIContextService> logger)
public unsafe WrappedPointer<T> GetFrameContext<T>(WrappedPointer<Frame> frame)
where T : unmanaged
{
if (frame.IsNull || frame.Pointer->FrameCallbacks.Size == 0)
{
return null;
}
for (uint i = 0; i < frame.Pointer->FrameCallbacks.Size; i++)
{
var callback = frame.Pointer->FrameCallbacks.Buffer[i];
if (callback.UiCtl_Context != null)
{
return new WrappedPointer<T>((T*)callback.UiCtl_Context);
}
}
return null;
return (T*)GWCA.GW.UI.GetFrameContext(frame.Pointer);
}
public unsafe WrappedPointer<Frame> GetChildFrame(WrappedPointer<Frame> parent, uint childOffset)
@@ -66,16 +51,16 @@ public sealed class UIContextService(ILogger<UIContextService> logger)
return false;
}
return GWCA.GW.UI.SendFrameUIMessage(frame, messageId, arg1, (nint)arg2);
return GWCA.GW.UI.SendFrameUIMessage(frame, (GWCA.GW.UI.UIMessage)messageId, arg1, (nint)arg2);
}
public unsafe bool SetFrameDisabled(WrappedPointer<Frame> frame, bool disabled) => GWCA.GW.UI.SetFrameDisabled(frame, disabled);
public unsafe bool SetFrameVisible(WrappedPointer<Frame> frame, bool visible) => GWCA.GW.UI.SetFrameVisible(frame, visible);
public unsafe bool KeyDown(ControlAction action, WrappedPointer<Frame> frame) => GWCA.GW.UI.Keydown(action, frame);
public unsafe bool KeyDown(GWCA.GW.UI.ControlAction action, WrappedPointer<Frame> frame) => GWCA.GW.UI.Keydown(action, frame);
public unsafe bool KeyUp(ControlAction action, WrappedPointer<Frame> frame) => GWCA.GW.UI.Keyup(action, frame);
public unsafe bool KeyUp(GWCA.GW.UI.ControlAction action, WrappedPointer<Frame> frame) => GWCA.GW.UI.Keyup(action, frame);
public unsafe WrappedPointer<Frame> GetFrameByLabel(string label)
{
@@ -107,9 +92,9 @@ public sealed class UIContextService(ILogger<UIContextService> logger)
return GWCA.GW.UI.ButtonClick(frame);
}
public unsafe void SendMessage(UIMessage message, nuint wParam, nuint lParam) => GWCA.GW.UI.SendUIMessage(message, (void*)wParam, (nint)lParam);
public unsafe void SendMessage(UIMessage message, nuint wParam, nuint lParam) => GWCA.GW.UI.SendUIMessage((GWCA.GW.UI.UIMessage)message, (void*)wParam, (nint)lParam);
public unsafe Task<string> AsyncDecodeStringAsync(ushort* encodedString, Language language = Language.Unknown, CancellationToken cancellationToken = default)
public unsafe Task<string> AsyncDecodeStringAsync(ushort* encodedString, GWCA.GW.Constants.Language language = GWCA.GW.Constants.Language.Unknown, CancellationToken cancellationToken = default)
{
var taskCompletionSource = new TaskCompletionSource<string>();
cancellationToken.Register(() => taskCompletionSource.TrySetCanceled(cancellationToken));
@@ -150,7 +135,7 @@ public sealed class UIContextService(ILogger<UIContextService> logger)
return taskCompletionSource.Task;
}
public unsafe Task<string> AsyncDecodeStringAsync(string encodedString, Language language = Language.Unknown, CancellationToken cancellationToken = default)
public unsafe Task<string> AsyncDecodeStringAsync(string encodedString, GWCA.GW.Constants.Language language = GWCA.GW.Constants.Language.Unknown, CancellationToken cancellationToken = default)
{
// Pin the string and convert to ushort*
fixed (char* encodedPtr = encodedString)
+24 -18
View File
@@ -1,8 +1,10 @@
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Services.Interop;
using Daybreak.Shared.Models.Api;
using Daybreak.Shared.Models.Guildwars;
using System.Extensions.Core;
using System.Runtime.CompilerServices;
using System.Text;
namespace Daybreak.API.Services;
@@ -32,45 +34,48 @@ public sealed class InventoryService(
return default;
}
var inventory = gameContext.Pointer->ItemContext->Inventory;
if (inventory.IsNull)
var inventory = gameContext.Pointer->Items->Inventory;
if (inventory is null)
{
scopedLogger.LogError("Inventory is null");
return default;
}
var itemTuples = new List<(BagType Type, List<(uint ModelId, string EncodedCompleteName, string EncodedSingleName, string EncodedName, bool Inscribable, int Quantity, uint[] Modifiers, ItemType ItemType, uint Interaction, uint ModelFileId)>)>();
foreach (var bag in inventory.Pointer->Bags)
var itemTuples = new List<(GWCA.GW.Constants.BagType Type, List<(uint ModelId, string EncodedCompleteName, string EncodedSingleName, string EncodedName, bool Inscribable, int Quantity, uint[] Modifiers, ItemType ItemType, uint Interaction, uint ModelFileId)>)>();
var bags = (BagStruct**)Unsafe.AsPointer(ref inventory->Bags);
for (var i = 0; i < 23; i++)
{
var bag = bags[i];
if (bag is null)
{
continue;
}
var retBag = new List<(uint ModelId, string EncodedCompleteName, string EncodedSingleName, string EncodedName, bool Inscribable, int Quantity, uint[] Modifiers, ItemType ItemType, uint Interaction, uint ModelFileId)>();
itemTuples.Add((bag->Type, retBag));
itemTuples.Add((bag->BagType, retBag));
if (bag->ItemsCount is 0)
{
continue;
}
foreach (var item in bag->Items)
foreach (var itemPtr in bag->Items.Value)
{
if (item.IsNull)
if (itemPtr is 0)
{
continue;
}
var singleItemName = new string(item.Pointer->SingleItemName);
var nameEncoded = new string(item.Pointer->NameEncoded);
var completeNameEncoded = new string(item.Pointer->CompleteNameEncoded);
var modifiers = new uint[item.Pointer->ModifiersCount];
for (var j = 0; j < item.Pointer->ModifiersCount; j++)
var item = (ItemStruct*)itemPtr;
var singleItemName = new string((char*)item->SingleItemName);
var nameEncoded = new string((char*)item->NameEnc);
var completeNameEncoded = new string((char*)item->CompleteNameEnc);
var modifiers = new uint[item->ModStructSize];
for (var j = 0; j < item->ModStructSize; j++)
{
modifiers[j] = item.Pointer->Modifiers[j].Mod;
modifiers[j] = item->ModStruct[j].Mod;
}
retBag.Add((item.Pointer->ModelId, completeNameEncoded, singleItemName, nameEncoded, item.Pointer->Inscribable, item.Pointer->Quantity, modifiers, item.Pointer->Type, item.Pointer->Interaction, item.Pointer->ModelFileId));
retBag.Add((item->ModelId, completeNameEncoded, singleItemName, nameEncoded, (item->Interaction & 0x08000000) != 0, item->Quantity, modifiers, (ItemType)item->Type, item->Interaction, item->ModelFileId));
}
}
@@ -85,17 +90,18 @@ public sealed class InventoryService(
// 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, uint Interaction, uint ModelFileId)> Items)>();
var decodedItemTuples = new List<(GWCA.GW.Constants.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, uint Interaction, uint ModelFileId)> Items)>();
foreach (var tuple in itemTuples)
{
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, uint Interaction, uint ModelFileId)>();
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);
var decodedName = await this.uIService.DecodeString(item.EncodedName, (GWCA.GW.Constants.Language)Language.English, cancellationToken);
var decodedCompleteName = await this.uIService.DecodeString(item.EncodedCompleteName, (GWCA.GW.Constants.Language)Language.English, cancellationToken);
var decodedSingleName = await this.uIService.DecodeString(item.EncodedSingleName, (GWCA.GW.Constants.Language)Language.English, cancellationToken);
decodedItems.Add((item.ModelId, item.EncodedName, decodedName, item.EncodedSingleName, decodedSingleName, item.EncodedCompleteName, decodedCompleteName, item.Inscribable, item.Quantity, item.Modifiers, item.ItemType, item.Interaction, item.ModelFileId));
}
decodedItemTuples.Add((tuple.Type, decodedItems));
}
+3 -5
View File
@@ -22,16 +22,14 @@ public sealed class LoginService(
unsafe
{
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull || gameContext.Pointer->CharContext is null)
if (gameContext.IsNull || gameContext.Pointer->Character is null)
{
scopedLogger.LogError("Game context is not initialized");
return default;
}
var playerNameSpan = gameContext.Pointer->CharContext->PlayerName.AsSpan();
var playerName = new string(playerNameSpan[..playerNameSpan.IndexOf('\0')]);
var emailSpan = gameContext.Pointer->CharContext->PlayerEmail.AsSpan();
var email = new string(emailSpan[..emailSpan.IndexOf('\0')]);
var playerName = new string(gameContext.Pointer->Character->PlayerName);
var email = new string(gameContext.Pointer->Character->PlayerEmail);
return new LoginInfo(email, playerName);
}
}, cancellationToken);
+100 -120
View File
@@ -1,4 +1,5 @@
using Daybreak.API.Extensions;
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using Daybreak.API.Services.Interop;
@@ -72,7 +73,7 @@ public sealed class MainPlayerService : IDisposable
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null)
gameContext.Pointer->World is null)
{
scopedLogger.LogError("Failed to get game context");
return false;
@@ -85,23 +86,23 @@ public sealed class MainPlayerService : IDisposable
return false;
}
var playerAgent = this.GetAgentContext(playerAgentId);
var playerAgent = this.agentContextService.GetAgentById(playerAgentId);
if (playerAgent is null ||
playerAgent->Type is not AgentType.Living ||
playerAgent->Type is not (uint)AgentType.Living ||
playerAgent->AgentId != playerAgentId)
{
scopedLogger.LogError("Player agent {playerAgentId} not found in agent array", playerAgentId);
return false;
}
var livingAgent = (AgentLivingContext*)playerAgent;
if (livingAgent->Level != gameContext.Pointer->WorldContext->Level)
var livingAgent = (AgentLiving*)playerAgent;
if (livingAgent->Level != gameContext.Pointer->World->Level)
{
scopedLogger.LogError("Player agent not found. Player level mismatch: {level} != {gameLevel}", livingAgent->Level, gameContext.Pointer->WorldContext->Level);
scopedLogger.LogError("Player agent not found. Player level mismatch: {level} != {gameLevel}", livingAgent->Level, gameContext.Pointer->World->Level);
return false;
}
var agentProfession = gameContext.Pointer->WorldContext->Professions.AsValueEnumerable().FirstOrDefault(p => p.AgentId == playerAgentId);
var agentProfession = gameContext.Pointer->World->PartyProfessionStates.AsValueEnumerable().FirstOrDefault(p => p.AgentId == playerAgentId);
if (agentProfession.AgentId != playerAgentId)
{
scopedLogger.LogError("Failed to find agent profession for player agent id {agentId}", playerAgentId);
@@ -112,9 +113,9 @@ public sealed class MainPlayerService : IDisposable
(uint)singleBuild.Primary.Id,
(uint)singleBuild.Secondary.Id,
singleBuild.Skills.AsValueEnumerable().Select(s => (uint)s.Id).ToArray(),
livingAgent->Primary,
agentProfession.UnlockedProfessionsFlags,
[.. gameContext.Pointer->WorldContext->UnlockedCharacterSkills]);
livingAgent->Tags->Primary,
agentProfession.UnlockedProfessions,
[.. gameContext.Pointer->World->UnlockedCharacterSkills]);
if (!this.buildTemplateManager.CanTemplateApply(validationRequest))
{
@@ -122,9 +123,9 @@ public sealed class MainPlayerService : IDisposable
return false;
}
var attributeIds = new Array12Uint();
var attributeIds = new AttributeArray12();
var attributeValues = new Array12Uint();
var skills = new Array8Uint();
var skills = new SkillIDArray8();
for (var i = 0; i < singleBuild.Attributes.Count && i < 12; i++)
{
if (singleBuild.Attributes[i].Attribute is null)
@@ -132,23 +133,24 @@ public sealed class MainPlayerService : IDisposable
continue;
}
attributeIds[i] = (uint)singleBuild.Attributes[i].Attribute!.Id;
attributeIds[i] = (GWCA.GW.Constants.Attribute)singleBuild.Attributes[i].Attribute!.Id;
attributeValues[i] = (uint)singleBuild.Attributes[i].Points;
}
for (var i = 0; i < singleBuild.Skills.Count && i < 8; i++)
{
skills[i] = (uint)singleBuild.Skills[i].Id;
skills[i] = (GWCA.GW.Constants.SkillID)singleBuild.Skills[i].Id;
}
var skillTemplate = new SkillTemplate(
(uint)singleBuild.Primary.Id,
(uint)singleBuild.Secondary.Id,
(uint)Math.Min(singleBuild.Attributes.Count, 12),
attributeIds,
attributeValues,
skills);
var skillTemplate = new SkillTemplate
{
Primary = (GWCA.GW.Constants.Profession)singleBuild.Primary.Id,
Secondary = (GWCA.GW.Constants.Profession)singleBuild.Secondary.Id,
AttributesCount = (uint)Math.Min(singleBuild.Attributes.Count, 12),
AttributeIds = attributeIds,
Skills = skills,
};
Unsafe.CopyBlock(skillTemplate.AttributeValues, Unsafe.AsPointer(ref attributeValues), (uint)(12 * sizeof(uint)));
this.skillbarContextService.LoadBuild(playerAgentId, &skillTemplate);
return true;
}
@@ -156,11 +158,11 @@ public sealed class MainPlayerService : IDisposable
if (result)
{
await this.chatService.AddMessageAsync("Build applied successfully.", "Daybreak.API", Channel.Moderator, cancellationToken);
await this.chatService.AddMessageAsync("Build applied successfully.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
}
else
{
await this.chatService.AddMessageAsync("Failed to apply build.", "Daybreak.API", Channel.Moderator, cancellationToken);
await this.chatService.AddMessageAsync("Failed to apply build.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
}
return result;
@@ -193,21 +195,21 @@ public sealed class MainPlayerService : IDisposable
return default;
}
var skillbarContext = gameContext.Pointer->WorldContext->Skillbars.AsValueEnumerable().FirstOrDefault(s => s.AgentId == playerAgentId);
var skillbarContext = gameContext.Pointer->World->Skillbar.Value.AsValueEnumerable().FirstOrDefault(s => s.AgentId == playerAgentId);
if (skillbarContext.AgentId != playerAgentId)
{
scopedLogger.LogError("Failed to find skillbar context for player agent id {agentId}", playerAgentId);
return default;
}
var agentAttributes = gameContext.Pointer->WorldContext->Attributes.AsValueEnumerable().FirstOrDefault(a => a.AgentId == playerAgentId);
var agentAttributes = gameContext.Pointer->World->Attributes.Value.AsValueEnumerable().FirstOrDefault(a => a.AgentId == playerAgentId);
if (agentAttributes.AgentId != playerAgentId)
{
scopedLogger.LogError("Failed to find agent attributes for player agent id {agentId}", playerAgentId);
return default;
}
var professionContext = gameContext.Pointer->WorldContext->Professions.AsValueEnumerable().FirstOrDefault(p => p.AgentId == playerAgentId);
var professionContext = gameContext.Pointer->World->PartyProfessionStates.AsValueEnumerable().FirstOrDefault(p => p.AgentId == playerAgentId);
if (professionContext.AgentId != playerAgentId)
{
scopedLogger.LogError("Failed to find agent profession for player agent id {agentId}", playerAgentId);
@@ -215,11 +217,11 @@ public sealed class MainPlayerService : IDisposable
}
return new BuildEntry(
(int)professionContext.CurrentPrimary,
(int)professionContext.CurrentSecondary,
agentAttributes.Attributes.GetAttributeEntryList(),
[skillbarContext.Skill0.Id, skillbarContext.Skill1.Id, skillbarContext.Skill2.Id, skillbarContext.Skill3.Id,
skillbarContext.Skill4.Id, skillbarContext.Skill5.Id, skillbarContext.Skill6.Id, skillbarContext.Skill7.Id]);
(int)professionContext.Primary,
(int)professionContext.Secondary,
[.. agentAttributes.Attribute.GetAttributeEntryList()],
[(uint)skillbarContext.Skills[0].SkillId, (uint)skillbarContext.Skills[1].SkillId, (uint)skillbarContext.Skills[2].SkillId, (uint)skillbarContext.Skills[3].SkillId,
(uint)skillbarContext.Skills[4].SkillId, (uint)skillbarContext.Skills[5].SkillId, (uint)skillbarContext.Skills[6].SkillId, (uint)skillbarContext.Skills[7].SkillId]);
}
}, cancellationToken);
}
@@ -238,15 +240,15 @@ public sealed class MainPlayerService : IDisposable
}
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull || gameContext.Pointer->WorldContext is null)
if (gameContext.IsNull || gameContext.Pointer->World is null)
{
scopedLogger.LogError("Game context is not initialized");
return default;
}
return new QuestLogInformation(
gameContext.Pointer->WorldContext->ActiveQuestId,
gameContext.Pointer->WorldContext->QuestLog.AsValueEnumerable().Select(q => new QuestInformation(q.QuestId, q.MapFrom)).ToList());
(uint)gameContext.Pointer->World->ActiveQuestId,
gameContext.Pointer->World->QuestLog.Value.AsValueEnumerable().Select(q => new QuestInformation((uint)q.QuestId, (uint)q.MapFrom)).ToList());
}
}, cancellationToken);
}
@@ -265,29 +267,27 @@ public sealed class MainPlayerService : IDisposable
}
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull || gameContext.Pointer->CharContext is null ||
gameContext.Pointer->WorldContext is null)
if (gameContext.IsNull || gameContext.Pointer->Character is null ||
gameContext.Pointer->World is null)
{
scopedLogger.LogError("Game context is not initialized");
return default;
}
var playerNameSpan = gameContext.Pointer->CharContext->PlayerName.AsSpan();
var playerName = new string(playerNameSpan[..playerNameSpan.IndexOf('\0')]);
var emailSpan = gameContext.Pointer->CharContext->PlayerEmail.AsSpan();
var email = new string(emailSpan[..emailSpan.IndexOf('\0')]);
var accountName = new string(gameContext.Pointer->WorldContext->AccountInfo->AccountName);
var playerName = new string (gameContext.Pointer->Character->PlayerName);
var email = new string(gameContext.Pointer->Character->PlayerEmail);
var accountName = new string((char*)gameContext.Pointer->World->AccountInfo->AccountName);
return new MainPlayerInformation(
gameContext.Pointer->CharContext->PlayerUuid.ToString(),
(*(Uuid*)gameContext.Pointer->Character->PlayerUuid).ToString(),
email,
playerName,
accountName,
gameContext.Pointer->WorldContext->AccountInfo->Wins,
gameContext.Pointer->WorldContext->AccountInfo->Losses,
gameContext.Pointer->WorldContext->AccountInfo->Rating,
gameContext.Pointer->WorldContext->AccountInfo->QualifierPoints,
gameContext.Pointer->WorldContext->AccountInfo->Rank,
gameContext.Pointer->WorldContext->AccountInfo->TournamentRewardPoints);
gameContext.Pointer->World->AccountInfo->Wins,
gameContext.Pointer->World->AccountInfo->Losses,
gameContext.Pointer->World->AccountInfo->Rating,
gameContext.Pointer->World->AccountInfo->QualifierPoints,
gameContext.Pointer->World->AccountInfo->Rank,
gameContext.Pointer->World->AccountInfo->TournamentRewardPoints);
}
}, cancellationToken);
}
@@ -306,61 +306,57 @@ public sealed class MainPlayerService : IDisposable
}
var gameContext = this.gameContextService.GetGameContext();
var agentArray = this.agentContextService.GetAgentArray();
var playerAgentId = this.agentContextService.GetPlayerAgentId();
if (gameContext.IsNull ||
gameContext.Pointer->WorldContext is null ||
gameContext.Pointer->CharContext is null ||
gameContext.Pointer->WorldContext->MapAgents.Buffer is null ||
agentArray.IsNull ||
agentArray.Pointer->Buffer is null ||
gameContext.Pointer->World is null ||
gameContext.Pointer->Character is null ||
playerAgentId is 0x0)
{
scopedLogger.LogDebug("Game data is not yet initialized");
return default;
}
var playerAgent = this.GetAgentContext(playerAgentId);
var playerAgent = this.agentContextService.GetAgentById(playerAgentId);
if (playerAgent is null ||
playerAgent->Type is not AgentType.Living ||
playerAgent->Type is not (uint)AgentType.Living ||
playerAgent->AgentId != playerAgentId)
{
scopedLogger.LogError("Player agent {playerAgentId} not found in agent array", playerAgentId);
return default;
}
var livingAgent = (AgentLivingContext*)playerAgent;
if (livingAgent->Level != gameContext.Pointer->WorldContext->Level)
var livingAgent = (AgentLiving*)playerAgent;
if (livingAgent->Level != gameContext.Pointer->World->Level)
{
scopedLogger.LogError("Player agent not found. Player level mismatch: {level} != {gameLevel}", livingAgent->Level, gameContext.Pointer->WorldContext->Level);
scopedLogger.LogError("Player agent not found. Player level mismatch: {level} != {gameLevel}", livingAgent->Level, gameContext.Pointer->World->Level);
return default;
}
return new MainPlayerState(
gameContext.Pointer->WorldContext->Experience,
gameContext.Pointer->WorldContext->Level,
gameContext.Pointer->World->Experience,
gameContext.Pointer->World->Level,
gameContext.Pointer->WorldContext->CurrentLuxon,
gameContext.Pointer->WorldContext->CurrentKurzick,
gameContext.Pointer->WorldContext->CurrentImperial,
gameContext.Pointer->WorldContext->CurrentBalthazar,
gameContext.Pointer->WorldContext->MaxLuxon,
gameContext.Pointer->WorldContext->MaxKurzick,
gameContext.Pointer->WorldContext->MaxImperial,
gameContext.Pointer->WorldContext->MaxBalthazar,
gameContext.Pointer->WorldContext->TotalLuxon,
gameContext.Pointer->WorldContext->TotalKurzick,
gameContext.Pointer->WorldContext->TotalImperial,
gameContext.Pointer->WorldContext->TotalBalthazar,
gameContext.Pointer->World->CurrentLuxon,
gameContext.Pointer->World->CurrentKurzick,
gameContext.Pointer->World->CurrentImperial,
gameContext.Pointer->World->CurrentBalth,
gameContext.Pointer->World->MaxLuxon,
gameContext.Pointer->World->MaxKurzick,
gameContext.Pointer->World->MaxImperial,
gameContext.Pointer->World->MaxBalth,
gameContext.Pointer->World->TotalEarnedLuxon,
gameContext.Pointer->World->TotalEarnedKurzick,
gameContext.Pointer->World->TotalEarnedImperial,
gameContext.Pointer->World->TotalEarnedBalth,
// Energy and health are percentages of Max
livingAgent->Health * livingAgent->MaxHealth,
livingAgent->MaxHealth,
livingAgent->Hp * livingAgent->MaxHp,
livingAgent->MaxHp,
livingAgent->Energy * livingAgent->MaxEnergy,
livingAgent->MaxEnergy,
livingAgent->Primary,
livingAgent->Secondary,
livingAgent->Tags->Primary,
livingAgent->Tags->Secondary,
playerAgent->Pos.X,
playerAgent->Pos.Y
@@ -386,18 +382,17 @@ public sealed class MainPlayerService : IDisposable
var currentMapInfo = this.instanceContextService.GetAreaInfo();
var serverRegion = this.instanceContextService.GetServerRegion();
if (gameContext.IsNull ||
gameContext.Pointer->CharContext is null ||
gameContext.Pointer->WorldContext is null ||
gameContext.Pointer->PartyContext is null ||
gameContext.Pointer->Character is null ||
gameContext.Pointer->World is null ||
gameContext.Pointer->Party is 0 ||
currentMapInfo.IsNull)
{
scopedLogger.LogError("Game context is not initialized");
return new InstanceInfo(0, 0, 0, 0, Shared.Models.Api.InstanceType.Loading, DistrictRegionInfo.Unknown, LanguageInfo.Unknown, CampaignInfo.Unknown, ContinentInfo.Unknown, RegionInfo.Unknown, DifficultyInfo.Unknown);
}
var charContext = *gameContext.Pointer->CharContext;
var worldContext = *gameContext.Pointer->WorldContext;
var partyContext = *gameContext.Pointer->PartyContext;
var charContext = *gameContext.Pointer->Character;
var worldContext = *gameContext.Pointer->World;
var mapInfo = *currentMapInfo.Pointer;
var mapId = charContext.MapId;
var language = charContext.Language;
@@ -406,7 +401,7 @@ public sealed class MainPlayerService : IDisposable
var foesToKill = worldContext.FoesToKill;
return new InstanceInfo(
MapId: charContext.MapId,
MapId: (uint)charContext.MapId,
DistrictNumber: charContext.DistrictNumber,
FoesKilled: worldContext.FoesKilled,
FoesToKill: worldContext.FoesToKill,
@@ -416,7 +411,7 @@ public sealed class MainPlayerService : IDisposable
Campaign: (CampaignInfo)mapInfo.Campaign,
Continent: (ContinentInfo)mapInfo.Continent,
Region: (RegionInfo)mapInfo.Region,
Difficulty: partyContext.Flags.HasFlag(PartyFlags.HardMode) ? DifficultyInfo.Hard : DifficultyInfo.Normal);
Difficulty: GWCA.GW.PartyMgr.GetIsPartyInHardMode() ? DifficultyInfo.Hard : DifficultyInfo.Normal);
}
}, cancellationToken);
}
@@ -435,27 +430,26 @@ public sealed class MainPlayerService : IDisposable
}
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull || gameContext.Pointer->WorldContext is null)
if (gameContext.IsNull || gameContext.Pointer->World is null)
{
scopedLogger.LogError("Game context is not initialized");
return default;
}
var playerIndex = gameContext.Pointer->CharContext->PlayerNumber;
var players = gameContext.Pointer->WorldContext->Players;
if (playerIndex < 0 || playerIndex >= players.Size)
var playerIndex = gameContext.Pointer->Character->PlayerNumber;
var player = GWCA.GW.PlayerMgr.GetPlayerByID(playerIndex);
if (player is null)
{
scopedLogger.LogError("Failed to get player id. Got {playerNumber}", playerIndex);
scopedLogger.LogError("Failed to get player");
return default;
}
var player = gameContext.Pointer->WorldContext->Players[(int)playerIndex];
var currentTier = player.ActiveTitleTier;
TitleContext? currentTitle = default;
var currentTier = player->ActiveTitleTier;
Title? currentTitle = default;
int? id = -1;
for (var i = 0; i < gameContext.Pointer->WorldContext->Titles.Size; i++)
for (var i = 0; i < gameContext.Pointer->World->Titles.Value.Size; i++)
{
var title = gameContext.Pointer->WorldContext->Titles.Skip(i).FirstOrDefault();
var title = gameContext.Pointer->World->Titles.Value.Skip(i).FirstOrDefault();
if (title.CurrentTitleTierIndex == currentTier)
{
currentTitle = title;
@@ -470,15 +464,15 @@ public sealed class MainPlayerService : IDisposable
return default;
}
var titleTier = gameContext.Pointer->WorldContext->TitleTiers.Skip((int)currentTitle.Value.CurrentTitleTierIndex).FirstOrDefault();
var titleTier = gameContext.Pointer->World->TitleTiers.Skip((int)currentTitle.Value.CurrentTitleTierIndex).FirstOrDefault();
return new TitleInfo
(
Id: (uint)id,
IsPercentage: currentTitle.Value.Props.HasFlag(TitleProps.PercentageBased),
PointsForCurrentRank: currentTitle.Value.PointsNeededForCurrentRank,
IsPercentage: currentTitle.Value.IsPercentageBased(),
PointsForCurrentRank: currentTitle.Value.PointsNeededCurrentRank,
PointsForNextRank: titleTier.TierNumber == currentTitle.Value.MaxTitleRank ?
currentTitle.Value.CurrentPoints :
currentTitle.Value.PointsNeededForNextRank,
currentTitle.Value.PointsNeededNextRank,
CurrentPoints: currentTitle.Value.CurrentPoints,
TierNumber: titleTier.TierNumber,
MaxTierNumber: currentTitle.Value.MaxTitleTierIndex
@@ -502,8 +496,8 @@ public sealed class MainPlayerService : IDisposable
var gameContext = this.gameContextService.GetGameContext();
if (gameContext.IsNull ||
gameContext.Pointer->AccountContext is null ||
gameContext.Pointer->WorldContext is null)
gameContext.Pointer->Account is null ||
gameContext.Pointer->World is null)
{
this.logger.LogError("Game context is not initialized");
return default;
@@ -516,7 +510,7 @@ public sealed class MainPlayerService : IDisposable
return default;
}
var agentProfession = gameContext.Pointer->WorldContext->Professions.AsValueEnumerable().FirstOrDefault(p => p.AgentId == playerAgentId);
var agentProfession = gameContext.Pointer->World->PartyProfessionStates.AsValueEnumerable().FirstOrDefault(p => p.AgentId == playerAgentId);
if (agentProfession.AgentId != playerAgentId)
{
scopedLogger.LogError("Failed to find agent profession for player agent id {agentId}", playerAgentId);
@@ -524,25 +518,11 @@ public sealed class MainPlayerService : IDisposable
}
return new MainPlayerBuildContext(
PrimaryProfessionId: (uint)agentProfession.CurrentPrimary,
UnlockedProfessions: agentProfession.UnlockedProfessionsFlags,
UnlockedAccountSkills: gameContext.Pointer->AccountContext->UnlockedAccountSkills.AsValueEnumerable().ToArray(),
UnlockedCharacterSkills: gameContext.Pointer->WorldContext->UnlockedCharacterSkills.AsValueEnumerable().ToArray());
PrimaryProfessionId: (uint)agentProfession.Primary,
UnlockedProfessions: agentProfession.UnlockedProfessions,
UnlockedCharacterSkills: gameContext.Pointer->World->UnlockedCharacterSkills.AsValueEnumerable().ToArray());
}
}, cancellationToken);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe AgentContext* GetAgentContext(uint agentId)
{
var agentArray = this.agentContextService.GetAgentArray();
if (agentArray.IsNull || agentArray.Pointer->Buffer is null ||
agentArray.Pointer->Size <= agentId)
{
return null;
}
return agentArray.Pointer->Buffer[agentId].Pointer;
}
}
+52 -55
View File
@@ -1,4 +1,5 @@
using Daybreak.API.Extensions;
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using Daybreak.API.Services.Interop;
@@ -9,6 +10,7 @@ using Daybreak.Shared.Services.BuildTemplates;
using System.Core.Extensions;
using System.Extensions;
using System.Extensions.Core;
using System.Runtime.CompilerServices;
using ZLinq;
using InstanceType = Daybreak.API.Interop.GuildWars.InstanceType;
@@ -46,21 +48,21 @@ public sealed class PartyService(
if (!await this.IsInValidOutpost(cancellationToken))
{
scopedLogger.LogError("Could not set party loadout. Not in a valid outpost");
await this.chatService.AddMessageAsync("Cannot set party loadout. Not in a valid outpost.", "Daybreak.API", Channel.Moderator, cancellationToken);
await this.chatService.AddMessageAsync("Cannot set party loadout. Not in a valid outpost.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
return false;
}
if (!await this.KickAllHeroes(cancellationToken))
{
scopedLogger.LogError("Could not set party loadout. Could not leave party");
await this.chatService.AddMessageAsync("Cannot set party loadout. Could not leave party.", "Daybreak.API", Channel.Moderator, cancellationToken);
await this.chatService.AddMessageAsync("Cannot set party loadout. Could not leave party.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
return false;
}
if (!await this.gameThreadService.QueueOnGameThread(() => this.SpawnHeroes(partyLoadout), cancellationToken))
{
scopedLogger.LogError("Could not set party loadout. Could not spawn heroes");
await this.chatService.AddMessageAsync("Cannot set party loadout. Could not spawn heroes.", "Daybreak.API", Channel.Moderator, cancellationToken);
await this.chatService.AddMessageAsync("Cannot set party loadout. Could not spawn heroes.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
return false;
}
@@ -69,21 +71,21 @@ public sealed class PartyService(
if (!await this.gameThreadService.QueueOnGameThread(() => this.ApplyBuilds(partyLoadout), cancellationToken))
{
scopedLogger.LogError("Could not set party loadout. Could not apply builds");
await this.chatService.AddMessageAsync("Cannot set party loadout. Could not apply builds.", "Daybreak.API", Channel.Moderator, cancellationToken);
await this.chatService.AddMessageAsync("Cannot set party loadout. Could not apply builds.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
return false;
}
var heroBehaviorSetup = await this.gameThreadService.QueueOnGameThread(() => this.GetHeroBehaviorSetup(partyLoadout), cancellationToken);
foreach (var heroBehaviorEntry in heroBehaviorSetup ?? [])
foreach (var (AgentId, Behavior) in heroBehaviorSetup ?? [])
{
if (!await this.SetHeroBehavior(heroBehaviorEntry.AgentId, heroBehaviorEntry.Behavior, cancellationToken))
if (!await this.SetHeroBehavior(AgentId, Behavior, cancellationToken))
{
scopedLogger.LogWarning("Could not set hero behavior for agent {agentId} to {behavior}", heroBehaviorEntry.AgentId, heroBehaviorEntry.Behavior);
await this.chatService.AddMessageAsync($"Cannot set party loadout. Could not set behavior {heroBehaviorEntry.Behavior} for agent {heroBehaviorEntry.AgentId}.", "Daybreak.API", Channel.Moderator, cancellationToken);
scopedLogger.LogWarning("Could not set hero behavior for agent {agentId} to {behavior}", AgentId, Behavior);
await this.chatService.AddMessageAsync($"Cannot set party loadout. Could not set behavior {Behavior} for agent {AgentId}.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
}
}
await this.chatService.AddMessageAsync("Party loadout set.", "Daybreak.API", Channel.Moderator, cancellationToken);
await this.chatService.AddMessageAsync("Party loadout set.", "Daybreak.API", Channel.CHANNEL_MODERATOR, cancellationToken);
return true;
}
@@ -126,9 +128,9 @@ public sealed class PartyService(
var buildTuples = professions.Value.AsValueEnumerable()
.Select(p => (p.AgentId, p, attributes.Value.FirstOrDefault(a => a.AgentId == p.AgentId), skillbars.Value.FirstOrDefault(s => s.AgentId == p.AgentId), heroFlags.Value.FirstOrDefault(f => f.AgentId == p.AgentId)))
.Select(t => (t.AgentId, GetBuildEntryById(t.p, t.Item4, t.Item3), t.Item5.Behavior))
.Select(t => (t.AgentId, GetBuildEntryById(t.p, t.Item4, t.Item3), t.Item5.HeroBehavior))
.Where(t => t.Item2 is not null)
.OfType<(uint AgentId, BuildEntry BuildEntry, Behavior Behavior)>()
.OfType<(uint AgentId, BuildEntry BuildEntry, API.Interop.GWCA.GW.HeroBehavior Behavior)>()
.ToList();
return new PartyLoadout(
@@ -136,16 +138,17 @@ public sealed class PartyService(
{
if (t.AgentId == playerId)
{
return new PartyLoadoutEntry(0, HeroBehavior.Undefined, t.BuildEntry);
return new PartyLoadoutEntry(0, Shared.Models.Api.HeroBehavior.Undefined, t.BuildEntry);
}
else if (heroes.Value.FirstOrDefault(h => h.AgentId == t.AgentId) is HeroPartyMember hero &&
hero.AgentId == t.AgentId)
{
return new PartyLoadoutEntry((int)hero.HeroId, (HeroBehavior)t.Behavior, t.BuildEntry);
return new PartyLoadoutEntry((int)hero.HeroId, (Shared.Models.Api.HeroBehavior)t.Behavior, t.BuildEntry);
}
return default;
})
.Where(t => t is not null)
.OfType<PartyLoadoutEntry>()]);
}
}, cancellationToken);
@@ -179,7 +182,7 @@ public sealed class PartyService(
}, cancellationToken);
}
public async Task<bool> SetHeroBehavior(uint heroAgentId, HeroBehavior behavior, CancellationToken cancellationToken)
public async Task<bool> SetHeroBehavior(uint heroAgentId, Shared.Models.Api.HeroBehavior behavior, CancellationToken cancellationToken)
{
var scopedLogger = this.logger.CreateScopedLogger();
@@ -209,14 +212,14 @@ public sealed class PartyService(
unsafe
{
var btn = this.uIContextService.GetChildFrame(frame.Frame, (uint)behavior);
var packet = new UIPackets.MouseClick(UIPackets.MouseButtons.Left, 0, 0);
var packet = new kMouseClick { MouseButton = 0, IsDoubleclick = 0 };
if (btn.IsNull)
{
scopedLogger.LogError("Could not set hero behavior. Could not find button for behavior {behavior} in frame {frameLabel}", behavior, heroCommanderFrameLabel);
return false;
}
return this.uIContextService.SendFrameUIMessage(btn, UIMessage.MouseClick, &packet);
return this.uIContextService.SendFrameUIMessage(btn, UIMessage.kMouseClick, &packet);
}
}, cancellationToken);
@@ -305,7 +308,7 @@ public sealed class PartyService(
}
else
{
var hero = heroes.Value.AsValueEnumerable().FirstOrDefault(h => h.HeroId == entry.HeroId);
var hero = heroes.Value.AsValueEnumerable().FirstOrDefault(h => (int)h.HeroId == entry.HeroId);
return (entry, hero.AgentId);
}
})
@@ -317,7 +320,7 @@ public sealed class PartyService(
: accountContext.Pointer->UnlockedAccountSkills;
var unlockedProfessionsFlags = t.playerId == playerId
? playerProfessionContext.UnlockedProfessionsFlags
? playerProfessionContext.UnlockedProfessions
: uint.MaxValue;
if (!this.buildTemplateManager.CanTemplateApply(
@@ -325,7 +328,7 @@ public sealed class PartyService(
(uint)t.entry.Build.Primary,
(uint)t.entry.Build.Secondary,
[.. t.entry.Build.Skills],
(uint)t.Item3.CurrentPrimary,
(uint)t.Item3.Primary,
unlockedProfessionsFlags,
[.. validSkills])))
{
@@ -358,28 +361,29 @@ public sealed class PartyService(
continue;
}
var attributeIds = new Array12Uint();
var attributeIds = new AttributeArray12();
var attributeValues = new Array12Uint();
for (var i = 0; i < entry.Build.Attributes.Count && i < 12; i++)
{
attributeIds[i] = (uint)entry.Build.Attributes[i].Id;
attributeIds[i] = (GWCA.GW.Constants.Attribute)entry.Build.Attributes[i].Id;
attributeValues[i] = (uint)entry.Build.Attributes[i].BasePoints;
}
var skills = new Array8Uint();
var skills = new SkillIDArray8();
for (var i = 0; i < entry.Build.Skills.Count && i < 8; i++)
{
skills[i] = entry.Build.Skills[i];
skills[i] = (GWCA.GW.Constants.SkillID)entry.Build.Skills[i];
}
var skillTemplate = new SkillTemplate(
(uint)entry.Build.Primary,
(uint)entry.Build.Secondary,
(uint)entry.Build.Attributes.Count,
attributeIds,
attributeValues,
skills);
var skillTemplate = new SkillTemplate
{
Primary = (GWCA.GW.Constants.Profession)entry.Build.Primary,
Secondary = (GWCA.GW.Constants.Profession)entry.Build.Secondary,
AttributesCount = (uint)entry.Build.Attributes.Count,
AttributeIds = attributeIds,
Skills = skills
};
Unsafe.CopyBlock(skillTemplate.AttributeValues, Unsafe.AsPointer(ref attributeValues), (uint)(12 * sizeof(uint)));
scopedLogger.LogInformation("Applying build for agent {agentId} with primary {primary} and secondary {secondary}", agentId, entry.Build.Primary, entry.Build.Secondary);
this.skillbarContextService.LoadBuild(agentId, &skillTemplate);
}
@@ -387,7 +391,7 @@ public sealed class PartyService(
return true;
}
private unsafe List<(uint AgentId, HeroBehavior Behavior)>? GetHeroBehaviorSetup(PartyLoadout partyLoadout)
private unsafe List<(uint AgentId, Shared.Models.Api.HeroBehavior Behavior)>? GetHeroBehaviorSetup(PartyLoadout partyLoadout)
{
var scopedLogger = this.logger.CreateScopedLogger();
scopedLogger.LogDebug("Getting hero behavior setup");
@@ -402,12 +406,12 @@ public sealed class PartyService(
return heroes.Value.AsValueEnumerable()
.Select(h =>
{
if (h.HeroId is 0)
if (h.HeroId is API.Interop.GWCA.GW.Constants.HeroID.NoHero)
{
return default;
}
var entry = partyLoadout.Entries.AsValueEnumerable().FirstOrDefault(e => (uint)e.HeroId == h.HeroId);
var entry = partyLoadout.Entries.AsValueEnumerable().FirstOrDefault(e => e.HeroId == (int)h.HeroId);
if (entry is null)
{
scopedLogger.LogWarning("No entry found for hero {heroId}", h.HeroId);
@@ -455,21 +459,14 @@ public sealed class PartyService(
return -1;
}
var partyContext = gameContext.Pointer->PartyContext;
if (partyContext is null)
{
scopedLogger.LogError("Party context is null");
return -1;
}
var charContext = gameContext.Pointer->CharContext;
var charContext = gameContext.Pointer->Character;
if (charContext is null)
{
scopedLogger.LogError("Char context is null");
return -1;
}
var playerParty = partyContext->PlayerParty;
var playerParty = GWCA.GW.PartyMgr.GetPartyInfo(0);
var playerId = charContext->PlayerNumber;
var offset = 0;
foreach(var hero in playerParty->Heroes)
@@ -492,22 +489,22 @@ public sealed class PartyService(
return heroNumber is -1 ? default : (uint)heroNumber;
}
private static BuildEntry? GetBuildEntryById(ProfessionsContext professionContext, SkillbarContext skillbar, PartyAttribute attributes)
private static BuildEntry? GetBuildEntryById(ProfessionState professionContext, SkillbarData skillbar, PartyAttribute attributes)
{
return new BuildEntry(
Primary: (int)professionContext.CurrentPrimary,
Secondary: (int)professionContext.CurrentSecondary,
Attributes: attributes.Attributes.GetAttributeEntryList(),
Primary: (int)professionContext.Primary,
Secondary: (int)professionContext.Secondary,
Attributes: attributes.Attribute.GetAttributeEntryList().ToList(),
Skills:
[
skillbar.Skill0.Id,
skillbar.Skill1.Id,
skillbar.Skill2.Id,
skillbar.Skill3.Id,
skillbar.Skill4.Id,
skillbar.Skill5.Id,
skillbar.Skill6.Id,
skillbar.Skill7.Id
(uint)skillbar.Skills[0].SkillId,
(uint)skillbar.Skills[1].SkillId,
(uint)skillbar.Skills[2].SkillId,
(uint)skillbar.Skills[3].SkillId,
(uint)skillbar.Skills[4].SkillId,
(uint)skillbar.Skills[5].SkillId,
(uint)skillbar.Skills[6].SkillId,
(uint)skillbar.Skills[7].SkillId
]);
}
}
+11 -12
View File
@@ -1,6 +1,5 @@
using Daybreak.API.Interop;
using Daybreak.API.Interop.GuildWars;
using Daybreak.API.Models;
using Daybreak.API.Services.Interop;
using System.Extensions.Core;
@@ -20,7 +19,7 @@ public sealed class UIService(
var scopedLogger = this.logger.CreateScopedLogger();
var keyDownResult = await this.gameThreadService.QueueOnGameThread(() =>
{
if (!this.uIContextService.KeyDown(action, frame))
if (!this.uIContextService.KeyDown((GWCA.GW.UI.ControlAction)action, frame))
{
scopedLogger.LogError("Failed to send keydown action {action}", action);
return false;
@@ -36,7 +35,7 @@ public sealed class UIService(
var keyUpResult = await this.gameThreadService.QueueOnGameThread(() =>
{
if (!this.uIContextService.KeyUp(action, frame))
if (!this.uIContextService.KeyUp((GWCA.GW.UI.ControlAction)action, frame))
{
scopedLogger.LogError("Failed to send keyup action {action}", action);
return false;
@@ -86,7 +85,7 @@ public sealed class UIService(
});
}
public async Task<string?> DecodeString(string encoded, Language language, CancellationToken cancellationToken)
public async Task<string?> DecodeString(string encoded, GWCA.GW.Constants.Language language, CancellationToken cancellationToken)
{
// GWCA's AsyncDecodeStr handles language switching internally (sets language, decodes, restores)
return await this.uIContextService.AsyncDecodeStringAsync(encoded, language, cancellationToken);
@@ -96,14 +95,14 @@ public sealed class UIService(
{
return frameLabel switch
{
"AgentCommander0" => ControlAction.OpenHeroCommander1,
"AgentCommander1" => ControlAction.OpenHeroCommander2,
"AgentCommander2" => ControlAction.OpenHeroCommander3,
"AgentCommander3" => ControlAction.OpenHeroCommander4,
"AgentCommander4" => ControlAction.OpenHeroCommander5,
"AgentCommander5" => ControlAction.OpenHeroCommander6,
"AgentCommander6" => ControlAction.OpenHeroCommander7,
_ => ControlAction.None
"AgentCommander0" => ControlAction.ControlAction_OpenHeroCommander1,
"AgentCommander1" => ControlAction.ControlAction_OpenHeroCommander2,
"AgentCommander2" => ControlAction.ControlAction_OpenHeroCommander3,
"AgentCommander3" => ControlAction.ControlAction_OpenHeroCommander4,
"AgentCommander4" => ControlAction.ControlAction_OpenHeroCommander5,
"AgentCommander5" => ControlAction.ControlAction_OpenHeroCommander6,
"AgentCommander6" => ControlAction.ControlAction_OpenHeroCommander7,
_ => ControlAction.ControlAction_None
};
}
}
@@ -1,6 +1,6 @@
using Daybreak.API.Services.Interop;
using Daybreak.Shared.Services.BuildTemplates;
using System.Diagnostics.CodeAnalysis;
using Daybreak.Shared.Services.BuildTemplates.Parsers;
namespace Daybreak.API.Services;
@@ -8,6 +8,9 @@ public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder WithDaybreakServices(this WebApplicationBuilder builder)
{
builder.Services.AddSingleton<ITemplateParser, LegacySkillTemplateParser>();
builder.Services.AddSingleton<ITemplateParser, SkillTemplateParser>();
builder.Services.AddSingleton<ITemplateParser, PartyLoadoutTemplateParser>();
builder.Services.AddSingleton<IBuildTemplateManager, BuildTemplateManager>();
builder.Services.AddSingleton<MemoryScanningService>();
builder.Services.AddSingleton<ChatService>();
-2
View File
@@ -488,7 +488,6 @@ public sealed class FocusViewModel(
}
if (this.BuildComponentContext?.CharacterUnlockedSkills.SequenceEqual(mainPlayerBuildContext.UnlockedCharacterSkills) is true &&
this.BuildComponentContext?.AccountUnlockedSkills.SequenceEqual(mainPlayerBuildContext.UnlockedAccountSkills) is true &&
this.BuildComponentContext?.UnlockedProfessions == mainPlayerBuildContext.UnlockedProfessions &&
this.BuildComponentContext?.PrimaryProfessionId == mainPlayerBuildContext.PrimaryProfessionId &&
DateTime.Now < this.lastBuildCheck + BuildsCheckFrequency)
@@ -518,7 +517,6 @@ public sealed class FocusViewModel(
{
IsInOutpost = instanceInfo.Type is Shared.Models.Api.InstanceType.Outpost,
PrimaryProfessionId = mainPlayerBuildContext.PrimaryProfessionId,
AccountUnlockedSkills = mainPlayerBuildContext.UnlockedAccountSkills,
CharacterUnlockedSkills = mainPlayerBuildContext.UnlockedCharacterSkills,
UnlockedProfessions = mainPlayerBuildContext.UnlockedProfessions,
AvailableBuilds = matchingBuilds
+863
View File
@@ -0,0 +1,863 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using static Daybreak.Generators.GenerateGWCABindings;
namespace Daybreak.Generators;
internal static class CppHeaderParser
{
// ════════════════════════════════════════════════════════════════
// Regex patterns for enum parsing
// ════════════════════════════════════════════════════════════════
// Matches: enum class Name : Type {
// Matches: enum class Name {
// Matches: enum Name : Type {
// Matches: enum {
private static readonly Regex EnumStartRegex = new(
@"^\s*enum\s+(?:class\s+)?(?:(\w+)\s*)?(?::\s*(\w+)\s*)?\{",
RegexOptions.Compiled);
// Matches: enum class Name : Type (brace on next line)
// Matches: enum class Name (brace on next line)
// Does NOT match forward declarations ending with ;
private static readonly Regex EnumNobraceRegex = new(
@"^\s*enum\s+(?:class\s+)?(?:(\w+)\s*)?(?::\s*(\w+)\s*)?$",
RegexOptions.Compiled);
// ════════════════════════════════════════════════════════════════
// Regex patterns for struct parsing
// ════════════════════════════════════════════════════════════════
// Matches: struct Name { or struct Name : public Base { or struct Name : Base {
// Captures: Name, optional BaseType
private static readonly Regex StructStartRegex = new(
@"^\s*struct\s+(\w+)\s*(?::\s*(?:public\s+)?(\w+(?:::\w+)*)\s*)?\{",
RegexOptions.Compiled);
// Matches: struct Name : public Base (brace on next line)
private static readonly Regex StructNobraceRegex = new(
@"^\s*struct\s+(\w+)\s*(?::\s*(?:public\s+)?(\w+(?:::\w+)*)\s*)?$",
RegexOptions.Compiled);
// Matches field with offset comment: /* +h0024 */ Type field;
private static readonly Regex FieldWithOffsetRegex = new(
@"^\s*/\*\s*\+h([0-9A-Fa-f]{4})\s*\*/\s*(.+?)\s*;\s*(?://\s*(.*))?$",
RegexOptions.Compiled);
// Matches field without offset comment: Type field;
// Also handles inline initializers like = value or {} or {value}
private static readonly Regex FieldNoOffsetRegex = new(
@"^\s*(?!(?:inline|static|virtual|GWCA_API|typedef|using|enum|struct|union|return|if|for|while|switch|\[\[|\}))([A-Za-z_][\w:*&<>\s,]+?)\s+(\w+)(?:\s*\[\s*([^\]]+)\s*\])?(?:\s*\{[^}]*\})?(?:\s*=\s*[^;]+)?\s*;\s*(?://\s*(.*))?$",
RegexOptions.Compiled);
// Matches static_assert(sizeof(Name) == 0xHEX or decimal)
private static readonly Regex SizeofAssertRegex = new(
@"static_assert\s*\(\s*sizeof\s*\(\s*(\w+)\s*\)\s*==\s*(0x[0-9A-Fa-f]+|\d+)",
RegexOptions.Compiled);
// ════════════════════════════════════════════════════════════════
// Other patterns
// ════════════════════════════════════════════════════════════════
// Matches: constexpr Type Name = Value; // comment
private static readonly Regex ConstexprRegex = new(
@"^\s*constexpr\s+(\w+)\s+(\w+)\s*=\s*(.+?)\s*;\s*(?://\s*(.*))?$",
RegexOptions.Compiled);
// Matches: namespace Name {
// Also matches: namespace Name1::Name2 { (but we split on ::)
private static readonly Regex NamespaceRegex = new(
@"^\s*namespace\s+([\w:]+)\s*\{",
RegexOptions.Compiled);
// Matches closing brace with optional semicolon
private static readonly Regex CloseBraceRegex = new(
@"^\s*\}\s*;?\s*$",
RegexOptions.Compiled);
// Matches: typedef ... (to skip)
private static readonly Regex TypedefRegex = new(
@"^\s*typedef\s+",
RegexOptions.Compiled);
// Matches lines to skip inside structs
private static readonly Regex SkipLineRegex = new(
@"^\s*(inline|static\s+const|GWCA_API|virtual|explicit|friend|\[\[nodiscard\]\]|constexpr|template\s*<|//|#|public:|private:|protected:|$)",
RegexOptions.Compiled);
// Matches: union { (to skip union blocks)
private static readonly Regex UnionStartRegex = new(
@"^\s*union\s*\{",
RegexOptions.Compiled);
// Matches: template<...> struct/class (to skip)
private static readonly Regex TemplateStructRegex = new(
@"^\s*template\s*<",
RegexOptions.Compiled);
// Matches inline function definitions (static, inline, GWCA_API, etc.) that have a body
private static readonly Regex FunctionWithBodyRegex = new(
@"^\s*(?:static|inline|GWCA_API|virtual|explicit|constexpr).*\([^)]*\)\s*(?:const)?\s*\{",
RegexOptions.Compiled);
// Matches function declarations with body on next line (no opening brace)
private static readonly Regex FunctionNoBraceRegex = new(
@"^\s*(?:static|inline|GWCA_API|virtual|explicit|constexpr).*\([^)]*\)\s*(?:const)?\s*$",
RegexOptions.Compiled);
// Matches out-of-line method definitions: Type* Class::Method() { or Type Class::Method() const {
private static readonly Regex OutOfLineMethodRegex = new(
@"^\s*(?:const\s+)?[\w:*&<>]+(?:\s+[\w:*&<>]+)*\s+\w+::\w+\s*\([^)]*\)\s*(?:const)?\s*\{",
RegexOptions.Compiled);
// Matches standalone opening brace
private static readonly Regex OpenBraceOnlyRegex = new(
@"^\s*\{\s*$",
RegexOptions.Compiled);
public static List<string> DebugLines { get; } = [];
public static void Parse(string headerText, ConstantsNode root)
{
var lines = headerText.Split('\n');
var namespaceStack = new Stack<ConstantsNode>();
namespaceStack.Push(root);
// Parsing state
bool inEnum = false;
CppEnumDef? currentEnum = null;
bool inStruct = false;
CppStructDef? currentStruct = null;
int structBraceDepth = 0;
bool inSkipBlock = false;
int skipBlockBraceCount = 0;
bool inMultiLineConstexpr = false;
int multiLineBraceCount = 0;
int freeBraceDepth = 0; // Track braces from functions with body on separate line
// Track struct sizes from static_assert
var structSizes = new Dictionary<string, int>(StringComparer.Ordinal);
// First pass: collect sizeof assertions
foreach (var line in lines)
{
var sizeMatch = SizeofAssertRegex.Match(line);
if (sizeMatch.Success)
{
var name = sizeMatch.Groups[1].Value;
var sizeStr = sizeMatch.Groups[2].Value;
int size = sizeStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
? Convert.ToInt32(sizeStr, 16)
: int.Parse(sizeStr);
structSizes[name] = size;
}
}
for (int i = 0; i < lines.Length; i++)
{
var line = lines[i].TrimEnd('\r');
// Skip preprocessor directives
if (line.TrimStart().StartsWith("#"))
continue;
var trimmed = line.TrimStart();
if (trimmed.Length == 0)
continue;
if (trimmed.StartsWith("//") && !inEnum && !inStruct)
continue;
// Handle multi-line constexpr (like constexpr std::array)
if (inMultiLineConstexpr)
{
multiLineBraceCount += CountChar(line, '{') - CountChar(line, '}');
if (multiLineBraceCount <= 0 || line.TrimEnd().EndsWith(";"))
{
inMultiLineConstexpr = false;
}
continue;
}
// Handle skip blocks (unions, template structs, etc.)
if (inSkipBlock)
{
skipBlockBraceCount += CountChar(line, '{') - CountChar(line, '}');
if (skipBlockBraceCount <= 0)
{
inSkipBlock = false;
}
continue;
}
// ── Inside an enum body (multi-line) ───────────────────
if (inEnum && currentEnum is not null)
{
if (trimmed.StartsWith("//"))
continue;
// Strip comments before checking for closing brace - comments can contain braces like "{ wchar_t* title }"
var trimmedWithoutComment = trimmed;
var commentIdx = trimmed.IndexOf("//");
if (commentIdx >= 0)
trimmedWithoutComment = trimmed.Substring(0, commentIdx).TrimEnd();
var closeBraceIdx = trimmedWithoutComment.IndexOf('}');
if (closeBraceIdx >= 0)
{
if (closeBraceIdx > 0)
{
ParseEnumMembersFromLine(trimmed.Substring(0, closeBraceIdx), currentEnum);
}
inEnum = false;
var node = namespaceStack.Peek();
node.Enums.Add(currentEnum);
currentEnum = null;
continue;
}
ParseEnumMembersFromLine(trimmed, currentEnum);
continue;
}
// ── Inside a struct body ───────────────────────────────
if (inStruct && currentStruct is not null)
{
// Strip comments before counting braces - comments can contain braces
var lineWithoutComments = StripComments(line);
int openBraces = CountChar(lineWithoutComments, '{');
int closeBraces = CountChar(lineWithoutComments, '}');
int prevDepth = structBraceDepth;
structBraceDepth += openBraces - closeBraces;
// Debug: Track brace changes for AgentLiving
if (currentStruct.Name == "AgentLiving" && (openBraces > 0 || closeBraces > 0))
{
DebugLines.Add($"[BRACE] line {i+1}: {currentStruct.Name} depth {prevDepth}->{structBraceDepth} (open={openBraces}, close={closeBraces})");
}
// If we hit the closing brace of the struct
if (structBraceDepth <= 0)
{
// Apply known size if available
if (structSizes.TryGetValue(currentStruct.Name, out var size))
currentStruct.Size = size;
var node = namespaceStack.Peek();
// Debug: capture namespace path
var pathParts = new List<string>();
foreach (var n in namespaceStack)
pathParts.Add(n.Name);
pathParts.Reverse();
currentStruct.DebugNamespacePath = string.Join(".", pathParts);
DebugLines.Add($"[STRUCT-END] line {i+1}: {currentStruct.Name} with {currentStruct.Fields.Count} fields");
node.Structs.Add(currentStruct);
currentStruct = null;
inStruct = false;
continue;
}
// Skip nested named structs (e.g., "struct Foo {") but NOT anonymous unions/structs
// We want to parse fields inside unions because they map to overlapping [FieldOffset] in C#
// Only skip if we're inside a named nested struct (not an anonymous union/struct block)
if (structBraceDepth > 1)
{
// Still try to parse fields with explicit offsets inside unions
// These are perfectly valid - they become overlapping fields in C# explicit layout
var fieldOffsetMatchNested = FieldWithOffsetRegex.Match(trimmed);
if (fieldOffsetMatchNested.Success)
{
var offset = Convert.ToInt32(fieldOffsetMatchNested.Groups[1].Value, 16);
var typeAndName = fieldOffsetMatchNested.Groups[2].Value;
var comment = fieldOffsetMatchNested.Groups[3].Success ? fieldOffsetMatchNested.Groups[3].Value.Trim() : null;
var field = ParseFieldTypeAndName(typeAndName, offset, comment);
if (field is not null)
currentStruct.Fields.Add(field);
}
continue;
}
// Skip comment-only lines
if (trimmed.StartsWith("//"))
continue;
// Skip method/static/inline lines
if (SkipLineRegex.IsMatch(trimmed))
continue;
// Skip lines with function bodies or method declarations
// Strip trailing // comment before checking - comments can contain parentheses like "(runes,pcons,etc)"
var trimmedWithoutTrailingComment = trimmed;
var trailingCommentIdx = trimmed.IndexOf("//");
if (trailingCommentIdx > 0)
trimmedWithoutTrailingComment = trimmed.Substring(0, trailingCommentIdx).TrimEnd();
if (trimmedWithoutTrailingComment.Contains("(") && (trimmedWithoutTrailingComment.Contains(")") || trimmedWithoutTrailingComment.Contains("{")))
continue;
// Skip bitfield declarations (e.g., DyeColor dye1 : 4;)
if (trimmed.Contains(" : ") && Regex.IsMatch(trimmed, @":\s*\d+\s*;"))
continue;
// Skip union starts
if (UnionStartRegex.IsMatch(trimmed))
continue;
// Try to parse field with offset
var fieldOffsetMatch = FieldWithOffsetRegex.Match(trimmed);
if (fieldOffsetMatch.Success)
{
var offset = Convert.ToInt32(fieldOffsetMatch.Groups[1].Value, 16);
var typeAndName = fieldOffsetMatch.Groups[2].Value;
var comment = fieldOffsetMatch.Groups[3].Success ? fieldOffsetMatch.Groups[3].Value.Trim() : null;
var field = ParseFieldTypeAndName(typeAndName, offset, comment);
if (field is not null)
currentStruct.Fields.Add(field);
continue;
}
// Try to parse field without offset
var fieldNoOffsetMatch = FieldNoOffsetRegex.Match(trimmed);
if (fieldNoOffsetMatch.Success)
{
var cppType = fieldNoOffsetMatch.Groups[1].Value.Trim();
var name = fieldNoOffsetMatch.Groups[2].Value;
var arraySize = fieldNoOffsetMatch.Groups[3].Success ? fieldNoOffsetMatch.Groups[3].Value.Trim() : null;
var comment = fieldNoOffsetMatch.Groups[4].Success ? fieldNoOffsetMatch.Groups[4].Value.Trim() : null;
var field = new CppStructField
{
Name = name,
CppType = cppType,
ArraySize = arraySize,
Comment = comment,
};
currentStruct.Fields.Add(field);
}
continue;
}
// ── Try to match namespace ─────────────────────────────
var nsMatch = NamespaceRegex.Match(trimmed);
if (nsMatch.Success)
{
var nsName = nsMatch.Groups[1].Value;
var parts = nsName.Split(new[] { "::" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
var current = namespaceStack.Peek();
var child = current.GetOrCreateChild(part);
namespaceStack.Push(child);
}
continue;
}
// ── Closing brace (function body end or namespace end) ─
if (CloseBraceRegex.IsMatch(trimmed))
{
// If we have unclosed function bodies, close those first
if (freeBraceDepth > 0)
{
freeBraceDepth--;
continue;
}
if (namespaceStack.Count > 1)
{
// Debug: Track where namespace was popped
var poppedNode = namespaceStack.Pop();
poppedNode.DebugPopLine = i + 1;
}
continue;
}
// ── Skip static_assert ─────────────────────────────────
if (trimmed.StartsWith("static_assert"))
{
if (!trimmed.Contains(";"))
{
for (int j = i + 1; j < lines.Length; j++)
{
if (lines[j].Contains(";"))
{
i = j;
break;
}
}
}
continue;
}
// ── Handle typedef struct Name { (parse as regular struct) ─
var typedefStructMatch = Regex.Match(trimmed, @"^\s*typedef\s+struct\s+(\w+)\s*\{");
if (typedefStructMatch.Success)
{
var structName = typedefStructMatch.Groups[1].Value;
currentStruct = new CppStructDef
{
Name = structName,
};
inStruct = true;
structBraceDepth = 1;
continue;
}
// ── Handle typedef Array<T> AliasName; ───────────────────────
var typedefArrayMatch = Regex.Match(trimmed, @"^\s*typedef\s+(\w+)\s*<\s*([\w:\s*]+)\s*>\s+(\w+)\s*;");
if (typedefArrayMatch.Success)
{
var sourceType = typedefArrayMatch.Groups[1].Value;
var templateArg = typedefArrayMatch.Groups[2].Value.Trim();
var aliasName = typedefArrayMatch.Groups[3].Value;
namespaceStack.Peek().TypeAliases.Add(new CppTypeAlias
{
AliasName = aliasName,
SourceType = sourceType,
TemplateArg = templateArg
});
continue;
}
// ── Skip other typedef ─────────────────────────────────
if (TypedefRegex.IsMatch(trimmed))
continue;
// ── Skip template struct/class ─────────────────────────
if (TemplateStructRegex.IsMatch(trimmed))
{
int braces = CountChar(trimmed, '{') - CountChar(trimmed, '}');
if (braces > 0)
{
inSkipBlock = true;
skipBlockBraceCount = braces;
}
continue;
}
// ── Skip inline function bodies ────────────────────────
if (FunctionWithBodyRegex.IsMatch(trimmed))
{
int braces = CountChar(trimmed, '{') - CountChar(trimmed, '}');
if (braces > 0)
{
inSkipBlock = true;
skipBlockBraceCount = braces;
}
continue;
}
// ── Skip out-of-line method definitions (Type* Class::Method() {) ─
if (OutOfLineMethodRegex.IsMatch(trimmed))
{
int braces = CountChar(trimmed, '{') - CountChar(trimmed, '}');
DebugLines.Add($"[OUT-OF-LINE] line {i+1}: {trimmed.Substring(0, Math.Min(50, trimmed.Length))}... braces={braces}");
if (braces > 0)
{
inSkipBlock = true;
skipBlockBraceCount = braces;
}
continue;
}
else if (trimmed.Contains("::") && trimmed.Contains("(") && trimmed.Contains("{"))
{
DebugLines.Add($"[UNMATCHED-METHOD] line {i+1}: {trimmed.Substring(0, Math.Min(60, trimmed.Length))}");
}
// ── Track function declarations with body on separate line ─
if (FunctionNoBraceRegex.IsMatch(trimmed))
{
// The next meaningful line should be { which starts the body
// We'll handle this by looking for standalone { and tracking brace depth
continue;
}
// ── Track standalone opening braces (function bodies with { on separate line)
if (OpenBraceOnlyRegex.IsMatch(trimmed))
{
freeBraceDepth++;
continue;
}
// ── Skip constexpr std::array etc. ─────────────────────
if (trimmed.StartsWith("constexpr std::"))
{
int braces = CountChar(trimmed, '{') - CountChar(trimmed, '}');
if (braces > 0)
{
inMultiLineConstexpr = true;
multiLineBraceCount = braces;
}
continue;
}
// ── Try to match struct start ──────────────────────────
var structMatch = StructStartRegex.Match(trimmed);
if (structMatch.Success)
{
var structName = structMatch.Groups[1].Value;
var baseType = structMatch.Groups[2].Success ? structMatch.Groups[2].Value : null;
DebugLines.Add($"[STRUCT-START] line {i+1}: {structName} (inSkip={inSkipBlock}, freeBrace={freeBraceDepth})");
// Skip some special cases
if (structName.StartsWith("__") || structName == "Packet")
{
int braces = CountChar(trimmed, '{') - CountChar(trimmed, '}');
if (braces > 0)
{
inSkipBlock = true;
skipBlockBraceCount = braces;
}
continue;
}
currentStruct = new CppStructDef
{
Name = structName,
BaseType = baseType,
};
inStruct = true;
structBraceDepth = 1;
continue;
}
// ── Struct without opening brace on same line ──────────
var structNobraceMatch = StructNobraceRegex.Match(trimmed);
if (structNobraceMatch.Success)
{
var structName = structNobraceMatch.Groups[1].Value;
// Skip forward declarations (just struct Name;)
if (trimmed.EndsWith(";"))
continue;
// Skip special cases
if (structName.StartsWith("__") || structName == "Packet")
continue;
// Look ahead for opening brace
for (int j = i + 1; j < lines.Length; j++)
{
var nextTrimmed = lines[j].TrimStart().TrimEnd('\r');
if (nextTrimmed.Length == 0 || nextTrimmed.StartsWith("//"))
continue;
if (nextTrimmed.Contains("{"))
{
var baseType = structNobraceMatch.Groups[2].Success ? structNobraceMatch.Groups[2].Value : null;
currentStruct = new CppStructDef
{
Name = structName,
BaseType = baseType,
};
inStruct = true;
structBraceDepth = 1;
i = j;
break;
}
break; // unexpected content
}
continue;
}
// ── Try to match enum start (with opening brace on same line) ──
var enumMatch = EnumStartRegex.Match(trimmed);
if (enumMatch.Success)
{
var enumDef = new CppEnumDef
{
Name = enumMatch.Groups[1].Success && enumMatch.Groups[1].Value.Length > 0
? enumMatch.Groups[1].Value : null,
UnderlyingType = enumMatch.Groups[2].Success && enumMatch.Groups[2].Value.Length > 0
? enumMatch.Groups[2].Value : null,
};
var afterBrace = trimmed.Substring(enumMatch.Length);
var closeBraceIdx = afterBrace.IndexOf('}');
if (closeBraceIdx >= 0)
{
var body = afterBrace.Substring(0, closeBraceIdx);
ParseEnumMembersFromLine(body, enumDef);
var node = namespaceStack.Peek();
node.Enums.Add(enumDef);
}
else
{
var rest = afterBrace.Trim();
if (rest.Length > 0)
{
ParseEnumMembersFromLine(rest, enumDef);
}
currentEnum = enumDef;
inEnum = true;
}
continue;
}
// ── Enum without opening brace on same line ────────────
var enumNobraceMatch = EnumNobraceRegex.Match(trimmed);
if (enumNobraceMatch.Success)
{
for (int j = i + 1; j < lines.Length; j++)
{
var nextTrimmed = lines[j].TrimStart().TrimEnd('\r');
if (nextTrimmed.Length == 0 || nextTrimmed.StartsWith("//"))
continue;
if (nextTrimmed.Contains("{"))
{
var enumDef = new CppEnumDef
{
Name = enumNobraceMatch.Groups[1].Success && enumNobraceMatch.Groups[1].Value.Length > 0
? enumNobraceMatch.Groups[1].Value : null,
UnderlyingType = enumNobraceMatch.Groups[2].Success && enumNobraceMatch.Groups[2].Value.Length > 0
? enumNobraceMatch.Groups[2].Value : null,
};
var braceIdx = nextTrimmed.IndexOf('{');
var afterBrace = nextTrimmed.Substring(braceIdx + 1);
var closeBraceIdx = afterBrace.IndexOf('}');
if (closeBraceIdx >= 0)
{
var body = afterBrace.Substring(0, closeBraceIdx);
ParseEnumMembersFromLine(body, enumDef);
var node = namespaceStack.Peek();
node.Enums.Add(enumDef);
}
else
{
var rest = afterBrace.Trim();
if (rest.Length > 0)
{
ParseEnumMembersFromLine(rest, enumDef);
}
currentEnum = enumDef;
inEnum = true;
}
i = j;
break;
}
break; // unexpected content
}
continue;
}
// ── Try to match constexpr field ───────────────────────
var constMatch = ConstexprRegex.Match(trimmed);
if (constMatch.Success)
{
var cppType = constMatch.Groups[1].Value;
var name = constMatch.Groups[2].Value;
var value = constMatch.Groups[3].Value.Trim();
var comment = constMatch.Groups[4].Success ? constMatch.Groups[4].Value.Trim() : null;
// Skip complex expressions with :: references or casts
if (value.Contains("::") || value.Contains("(") || value.Contains("static_cast"))
continue;
var field = new CppConstField
{
Name = name,
CppType = cppType,
Value = NormalizeCppValue(value),
Comment = comment,
};
var node = namespaceStack.Peek();
node.Constants.Add(field);
continue;
}
}
}
/// <summary>
/// Parses a combined "Type Name" or "Type Name[Size]" string into a field.
/// </summary>
private static CppStructField? ParseFieldTypeAndName(string typeAndName, int offset, string? comment)
{
typeAndName = typeAndName.Trim();
// Handle array notation: uint32_t name[7]
string? arraySize = null;
var bracketIdx = typeAndName.IndexOf('[');
if (bracketIdx >= 0)
{
var closeBracket = typeAndName.IndexOf(']', bracketIdx);
if (closeBracket > bracketIdx)
{
arraySize = typeAndName.Substring(bracketIdx + 1, closeBracket - bracketIdx - 1).Trim();
typeAndName = typeAndName.Substring(0, bracketIdx).Trim();
}
}
// Split type and name - name is the last token
var tokens = typeAndName.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length < 2)
return null;
var name = tokens[tokens.Length - 1].TrimStart('*');
var cppType = string.Join(" ", tokens.Take(tokens.Length - 1));
// Handle pointer attached to name: Item *weapon -> Item*, weapon
if (tokens[tokens.Length - 1].StartsWith("*"))
{
cppType += "*";
}
// Handle pointer attached to type: Item* weapon
else if (cppType.EndsWith("*"))
{
// Already correct
}
// Handle pointer in middle: Item * weapon
else if (tokens.Any(t => t == "*"))
{
cppType = cppType.Replace(" *", "*").Replace("* ", "*");
if (!cppType.EndsWith("*"))
cppType += "*";
}
return new CppStructField
{
Name = name,
CppType = cppType.Trim(),
Offset = offset,
ArraySize = arraySize,
Comment = comment,
};
}
/// <summary>
/// Parses comma-separated enum members from a single line of text.
/// </summary>
private static void ParseEnumMembersFromLine(string line, CppEnumDef enumDef)
{
string? lineComment = null;
var commentIdx = line.IndexOf("//");
if (commentIdx >= 0)
{
lineComment = line.Substring(commentIdx + 2).Trim();
line = line.Substring(0, commentIdx);
}
var parts = line.Split(',');
CppEnumMember? lastMember = null;
foreach (var part in parts)
{
var trimPart = part.Trim();
if (trimPart.Length == 0)
continue;
var eqIdx = trimPart.IndexOf('=');
string name;
string? value = null;
if (eqIdx >= 0)
{
name = trimPart.Substring(0, eqIdx).Trim();
value = trimPart.Substring(eqIdx + 1).Trim();
}
else
{
name = trimPart;
}
if (name.Length > 0 && (char.IsLetter(name[0]) || name[0] == '_'))
{
var member = new CppEnumMember
{
Name = name,
Value = value,
};
enumDef.Members.Add(member);
lastMember = member;
}
}
if (lastMember is not null && lineComment is not null)
{
lastMember.Comment = lineComment;
}
}
/// <summary>
/// Strips C++ comments from a line (both // and /* */ style).
/// </summary>
private static string StripComments(string line)
{
// Remove // style comments
int slashSlash = line.IndexOf("//");
if (slashSlash >= 0)
line = line.Substring(0, slashSlash);
// Remove /* */ style comments (simple - doesn't handle nested)
int blockStart = line.IndexOf("/*");
while (blockStart >= 0)
{
int blockEnd = line.IndexOf("*/", blockStart + 2);
if (blockEnd >= 0)
line = line.Substring(0, blockStart) + line.Substring(blockEnd + 2);
else
line = line.Substring(0, blockStart);
blockStart = line.IndexOf("/*");
}
return line;
}
private static int CountChar(string s, char c)
{
int count = 0;
foreach (var ch in s)
if (ch == c) count++;
return count;
}
/// <summary>
/// Normalizes a C++ value literal to a C# compatible one.
/// </summary>
private static string NormalizeCppValue(string value)
{
value = value.Trim().TrimEnd(',');
// Remove 'u' or 'U' suffix from integer literals
if (value.EndsWith("u", StringComparison.OrdinalIgnoreCase) &&
!value.EndsWith("0xu", StringComparison.OrdinalIgnoreCase))
{
var candidate = value.Substring(0, value.Length - 1);
if (IsIntegerLiteral(candidate))
return candidate;
}
// Normalize C++ float suffix to valid C# float literal
if (value.EndsWith("f", StringComparison.OrdinalIgnoreCase))
{
value = value.Substring(0, value.Length - 1);
if (value.EndsWith("."))
value += "0";
return value + "f";
}
// If it's a float-looking literal without suffix, add f
if (value.Contains(".") && !value.Contains("\""))
return value + "f";
return value;
}
private static bool IsIntegerLiteral(string s)
{
s = s.Trim();
if (s.Length == 0) return false;
if (s.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
return s.Length > 2 && s.Substring(2).All(c => "0123456789abcdefABCDEF".Contains(c));
var start = s[0] == '-' ? 1 : 0;
if (start >= s.Length) return false;
return s.Substring(start).All(char.IsDigit);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
namespace Daybreak.Shared.Models.Api;
public sealed record MainPlayerBuildContext(uint PrimaryProfessionId, uint UnlockedProfessions, uint[] UnlockedCharacterSkills, uint[] UnlockedAccountSkills)
public sealed record MainPlayerBuildContext(uint PrimaryProfessionId, uint UnlockedProfessions, uint[] UnlockedCharacterSkills)
{
}
@@ -7,6 +7,5 @@ public sealed class BuildComponentContext
public required uint PrimaryProfessionId { get; init; }
public required uint UnlockedProfessions { get; init; }
public required uint[] CharacterUnlockedSkills { get; init; }
public required uint[] AccountUnlockedSkills { get; init; }
public required List<IBuildEntry> AvailableBuilds { get; init; }
}
+1 -1
View File
@@ -48,6 +48,6 @@
<Platform Project="x64" />
</Project>
<Project Path="Daybreak.Generators/Daybreak.Generators.csproj">
<Platform Project="x64" />
<Platform Project="Any CPU" />
</Project>
</Solution>
+518
View File
@@ -0,0 +1,518 @@
#pragma once
namespace GW
{
namespace Constants
{
namespace ModelID
{
// this is actually agent->PlayerNumber for agents
namespace Minipet
{
// First Year
constexpr int Charr = 230; // Miniature Charr Shaman (purple)
constexpr int Dragon = 231; // Miniature Bone Dragon (green)
constexpr int Rurik = 232; // Miniature Prince Rurik (gold)
constexpr int Shiro = 233; // Miniature Shiro (gold)
constexpr int Titan = 234; // Miniature Burning Titan (purple)
constexpr int Kirin = 235; // Miniature Kirin (purple)
constexpr int NecridHorseman = 236; // Miniature Necrid Horseman (white)
constexpr int JadeArmor = 237; // Miniature Jade Armor (white)
constexpr int Hydra = 238; // Miniature Hydra (white)
constexpr int FungalWallow = 239; // Miniature Fungal Wallow (white)
constexpr int SiegeTurtle = 240; // Miniature Siege Turtle (white)
constexpr int TempleGuardian = 241; // Miniature Temple Guardian (white)
constexpr int JungleTroll = 242; // Miniature Jungle Troll (white)
constexpr int WhiptailDevourer = 243; // Miniature Whiptail Devourer (white)
// Second Year
constexpr int Gwen = 244; // Miniature Gwen (green)
constexpr int GwenDoll = 245; // Miniature Gwen Doll (??)
constexpr int WaterDjinn = 246; // Miniature Water Djinn (gold)
constexpr int Lich = 247; // Miniature Lich (gold)
constexpr int Elf = 248; // Miniature Elf (purple)
constexpr int PalawaJoko = 249; // Miniature Palawa Joko (purple)
constexpr int Koss = 250; // Miniature Koss (purple)
constexpr int MandragorImp = 251; // Miniature Mandragor Imp (white)
constexpr int HeketWarrior = 252; // Miniature Heket Warrior (white)
constexpr int HarpyRanger = 253; // Miniature Harpy Ranger (white)
constexpr int Juggernaut = 254; // Miniature Juggernaut (white)
constexpr int WindRider = 255; // Miniature Wind Rider (white)
constexpr int FireImp = 256; // Miniature Fire Imp (white)
constexpr int Aatxe = 257; // Miniature Aatxe (white)
constexpr int ThornWolf = 258; // Miniature Thorn Wolf (white)
// Third Year
constexpr int Abyssal = 259; // Miniature Abyssal (white)
constexpr int BlackBeast = 260; // Miniature Black Beast of Aaaaarrrrrrgghh (gold)
constexpr int Freezie = 261; // Miniature Freezie (purple)
constexpr int Irukandji = 262; // Miniature Irukandji (white)
constexpr int MadKingThorn = 263; // Miniature Mad King Thorn (green)
constexpr int ForestMinotaur = 264; // Miniature Forest Minotaur (white)
constexpr int Mursaat = 265; // Miniature Mursaat (white)
constexpr int Nornbear = 266; // Miniature Nornbear (purple)
constexpr int Ooze = 267; // Miniature Ooze (purple)
constexpr int Raptor = 268; // Miniature Raptor (white)
constexpr int RoaringEther = 269; // Miniature Roaring Ether (white)
constexpr int CloudtouchedSimian = 270; // Miniature Cloudtouched Simian (white)
constexpr int CaveSpider = 271; // Miniature Cave Spider (white)
constexpr int WhiteRabbit = 272; // White Rabbit (gold)
// Fourth Year
constexpr int WordofMadness = 273; // Miniature Word of Madness (white)
constexpr int DredgeBrute = 274; // Miniature Dredge Brute (white)
constexpr int TerrorwebDryder = 275; // Miniature Terrorweb Dryder (white)
constexpr int Abomination = 276; // Miniature Abomination (white)
constexpr int KraitNeoss = 277; // Miniature Krait Neoss (white)
constexpr int DesertGriffon = 278; // Miniature Desert Griffon (white)
constexpr int Kveldulf = 279; // Miniature Kveldulf (white)
constexpr int QuetzalSly = 280; // Miniature Quetzal Sly (white)
constexpr int Jora = 281; // Miniature Jora (purple)
constexpr int FlowstoneElemental = 282; // Miniature Flowstone Elemental (purple)
constexpr int Nian = 283; // Miniature Nian (purple)
constexpr int DagnarStonepate = 284; // Miniature Dagnar Stonepate (gold)
constexpr int FlameDjinn = 285; // Miniature Flame Djinn (gold)
constexpr int EyeOfJanthir = 286; // Miniature Eye of Janthir (green)
// Fifth Year
constexpr int Seer = 287; // Miniature Seer (white)
constexpr int SiegeDevourer = 288; // Miniature Siege Devourer (white)
constexpr int ShardWolf = 289; // Miniature Shard Wolf (white)
constexpr int FireDrake = 290; // Miniature Fire Drake (white)
constexpr int SummitGiantHerder = 291; // Miniature Summit Giant Herder (white)
constexpr int OphilNahualli = 292; // Miniature Ophil Nahualli (white)
constexpr int CobaltScabara = 293; // Miniature Cobalt Scabara (white)
constexpr int ScourgeManta = 294; // Miniature Scourge Manta (white)
constexpr int Ventari = 295; // Miniature Ventari (purple)
constexpr int Oola = 296; // Miniature Oola (purple)
constexpr int CandysmithMarley = 297; // Miniature CandysmithMarley (purple)
constexpr int ZhuHanuku = 298; // Miniature Zhu Hanuku (gold)
constexpr int KingAdelbern = 299; // Miniature King Adelbern (gold)
constexpr int MOX1 = 300; // Miniature M.O.X. (color?)
constexpr int MOX2 = 301; // Miniature M.O.X. (color?)
constexpr int MOX3 = 302; // Miniature M.O.X. (green)
constexpr int MOX4 = 303; // Miniature M.O.X. (color?)
constexpr int MOX5 = 304; // Miniature M.O.X. (color?)
constexpr int MOX6 = 305; // Miniature M.O.X. (color?)
// In-game rewards and promotionals
constexpr int BrownRabbit = 306; // Miniature Brown Rabbit
constexpr int Yakkington = 307; // Miniature Yakkington
// constexpr int Unknown308 = 308;
constexpr int CollectorsEditionKuunavang = 309; // Miniature Kuunavang (green)
constexpr int GrayGiant = 310;
constexpr int Asura = 311;
constexpr int DestroyerOfFlesh = 312;
constexpr int PolarBear = 313;
constexpr int CollectorsEditionVaresh = 314;
constexpr int Mallyx = 315;
constexpr int Ceratadon = 316;
// Misc.
constexpr int Kanaxai = 317;
constexpr int Panda = 318;
constexpr int IslandGuardian = 319;
constexpr int NagaRaincaller = 320;
constexpr int LonghairYeti = 321;
constexpr int Oni = 322;
constexpr int ShirokenAssassin = 323;
constexpr int Vizu = 324;
constexpr int ZhedShadowhoof = 325;
constexpr int Grawl = 326;
constexpr int GhostlyHero = 327;
// Special events
constexpr int Pig = 328;
constexpr int GreasedLightning = 329;
constexpr int WorldFamousRacingBeetle = 330;
constexpr int CelestialPig = 331;
constexpr int CelestialRat = 332;
constexpr int CelestialOx = 333;
constexpr int CelestialTiger = 334;
constexpr int CelestialRabbit = 335;
constexpr int CelestialDragon = 336;
constexpr int CelestialSnake = 337;
constexpr int CelestialHorse = 338;
constexpr int CelestialSheep = 339;
constexpr int CelestialMonkey = 340;
constexpr int CelestialRooster = 341;
constexpr int CelestialDog = 342;
// In-game
constexpr int BlackMoaChick = 343;
constexpr int Dhuum = 344;
constexpr int MadKingsGuard = 345;
constexpr int SmiteCrawler = 346;
// Zaishen strongboxes, and targetable minipets
constexpr int GuildLord = 347;
constexpr int HighPriestZhang = 348;
constexpr int GhostlyPriest = 349;
constexpr int RiftWarden = 350;
}
namespace SummoningStone
{
constexpr int MercantileMerchant = 467;
constexpr int ZaishenArcher = 468;
constexpr int ZaishenAvatarOfBalthazar = 469;
constexpr int ZaishenChampionOfBalthazar = 470;
constexpr int ZaishenPriestOfBalthazar = 471;
constexpr int ZaishenFootman = 472;
constexpr int ZaishenGuildLord = 473;
constexpr int MysteriousCrystalSpider = 490;
constexpr int MysteriousSaltsprayDragon = 491;
constexpr int MysteriousRestlessCorpse = 492;
constexpr int MysteriousSmokePhantom = 493;
constexpr int MysteriousSwarmOfBees = 494;
constexpr int AutomatonGolem = 512;
constexpr int IgneousFireImp = 513;
constexpr int JadeiteSiegeTurtle = 514;
constexpr int DemonicOni = 515;
constexpr int AmberJuggernaut = 516;
constexpr int MysticalGaki = 517;
constexpr int GelatinousOoze = 518;
constexpr int ChitinousDevourer = 519;
constexpr int ArcticKveldulf = 520;
constexpr int FossilizedRaptor = 521;
constexpr int MischievousGrentch = 522;
constexpr int FrostySnowman = 523;
constexpr int GhastlyDreamRider = 524;
}
// New bosses 2020-04-22
enum
{
AbaddonsCursed = 1338,
BladeAncientSyuShai = 1339,
YoannhTheRebuilber = 1340,
FureystSharpsight = 1341
};
namespace FoW
{
constexpr int NimrosTheHunter = 1485;
constexpr int MikoTheUnchained = 2015;
}
namespace UW
{
constexpr int ChainedSoul = 2317;
constexpr int DyingNightmare = 2318;
constexpr int ObsidianBehemoth = 2319;
constexpr int ObsidianGuardian = 2320;
constexpr int TerrorwebDryder = 2321;
constexpr int TerrorwebDryderSilver = 2322;
constexpr int KeeperOfSouls = 2323;
constexpr int TerrorwebQueen = 2324; // boss-like
constexpr int SmiteCrawler = 2325;
constexpr int WailingLord = 2326; // Note: same as FoW::Banshee
constexpr int BanishedDreamRider = 2327;
// 2324 ?
constexpr int FourHorseman = 2329; // all four share the same id
constexpr int MindbladeSpectre = 2330;
constexpr int DeadCollector = 2332;
constexpr int DeadThresher = 2333;
constexpr int ColdfireNight = 2334;
constexpr int StalkingNight = 2335;
// 2332 ?
constexpr int ChargedBlackness = 2337;
constexpr int GraspingDarkness = 2338;
constexpr int BladedAatxe = 2339;
// 2336 ?
constexpr int Slayer = 2391;
constexpr int SkeletonOfDhuum1 = 2392;
constexpr int SkeletonOfDhuum2 = 2393;
constexpr int ChampionOfDhuum = 2394;
constexpr int MinionOfDhuum = 2395;
constexpr int Dhuum = 2396;
constexpr int Reapers = 2399; // outside dhuum chamber
constexpr int ReapersAtDhuum = 2400; // in dhuum chamber
constexpr int IceElemental = 2401; // friendly, during waste quest near dhuum.
constexpr int KingFrozenwind = 2403;
constexpr int TorturedSpirit1 = 2404; // friendly, during quest
constexpr int Escort1 = 2407; // souls npc spawned by escort quest
constexpr int Escort2 = 2408;
constexpr int Escort3 = 2409;
constexpr int Escort4 = 2410;
constexpr int Escort5 = 2411;
constexpr int Escort6 = 2412;
constexpr int PitsSoul1 = 2413;
constexpr int PitsSoul2 = 2414;
constexpr int PitsSoul3 = 2415;
constexpr int PitsSoul4 = 2416;
constexpr int TorturedSpirit = 2422;
constexpr int MajorAlgheri = 2424;
}
namespace FoW
{
constexpr int Banshee = 2377; // Note: same as UW::WailingLord
constexpr int MahgoHydra = 2847;
constexpr int ArmoredCaveSpider = 2851;
constexpr int SmokeWalker = 2852;
constexpr int ObsidianFurnanceDrake = 2853;
constexpr int DoubtersDryder = 2854;
constexpr int ShadowMesmer = 2855;
constexpr int ShadowElemental = 2856;
constexpr int ShadowMonk = 2857;
constexpr int ShadowWarrior = 2858;
constexpr int ShadowRanger = 2859;
constexpr int ShadowBeast = 2860;
constexpr int Abyssal = 2861; // Note: same as ShadowOverlord.
constexpr int ShadowOverlord = 2861; // Note: same as Abyssal.
constexpr int SeedOfCorruption = 2862;
constexpr int SpiritWood = 2863;
constexpr int SpiritShepherd = 2864;
constexpr int AncientSkale = 2865;
constexpr int SnarlingDriftwood = 2866;
constexpr int SkeletalEtherBreaker = 2867;
constexpr int SkeletalIcehand = 2868;
constexpr int SkeletalBond = 2869;
constexpr int SkeletalBerserker = 2870;
constexpr int SkeletalImpaler = 2871;
constexpr int RockBorerWorm = 2872;
constexpr int InfernalWurm = 2873;
constexpr int DragonLich = 2874;
constexpr int Menzies = 2875;
// 2821 ?
constexpr int Rastigan = 2877; // Friendly NPC
constexpr int Griffons = 2878; // Friendly NPC
constexpr int LordKhobay = 2879; // Unfriendly NPC
constexpr int Forgemaster = 2880; // Friendly NPC
// 2826 ?
constexpr int TraitorousTempleGuard1 = 2882;
constexpr int TraitorousTempleGuard2 = 2883;
constexpr int TraitorousTempleGuard3 = 2884;
// 2830 ?
constexpr int ShardWolf = 2886;
}
constexpr int Rotscale = 2888;
constexpr int Winnowing = 2926;
constexpr int EoE = 2927;
constexpr int FrozenSoil = 2933;
constexpr int QZ = 2937;
namespace Urgoz
{
constexpr int HoppingVampire = 3796;
constexpr int Urgoz = 3805;
}
namespace Deep
{
constexpr int Kanaxai = 4110;
constexpr int KanaxaiAspect = 4111;
}
constexpr int Lacerate = 4283;
constexpr int Equinox = 4287;
constexpr int Famine = 4289;
namespace DoA
{
// Friendly
constexpr int FoundrySnakes = 5272;
constexpr int BlackBeastOfArgh = 5201;
constexpr int SmotheringTendril = 5265;
constexpr int Fury = 5200;
constexpr int LordJadoth = 5195;
// stygian lords
constexpr int StygianLordNecro = 5196;
constexpr int StygianLordMesmer = 5197;
constexpr int StygianLordEle = 5198;
constexpr int StygianLordMonk = 5199;
constexpr int StygianLordDerv = 5214;
constexpr int StygianLordRanger = 5215;
// margonites
constexpr int MargoniteAnurKaya = 5217;
constexpr int MargoniteAnurDabi = 5218;
constexpr int MargoniteAnurSu = 5219;
constexpr int MargoniteAnurKi = 5220;
constexpr int MargoniteAnurVu = 5221;
constexpr int MargoniteAnurTuk = 5222;
constexpr int MargoniteAnurRuk = 5223;
constexpr int MargoniteAnurRund = 5224;
constexpr int MargoniteAnurMank = 5225;
// stygians
constexpr int StygianHunger = 5226;
constexpr int StygianBrute = 5227;
constexpr int StygianGolem = 5228;
constexpr int StygianHorror = 5229;
constexpr int StygianFiend = 5230;
// tormentors in veil
constexpr int VeilMindTormentor = 5231;
constexpr int VeilSoulTormentor = 5232;
constexpr int VeilWaterTormentor = 5233;
constexpr int VeilHeartTormentor = 5234;
constexpr int VeilFleshTormentor = 5235;
constexpr int VeilSpiritTormentor = 5236;
constexpr int VeilEarthTormentor = 5237;
constexpr int VeilSanityTormentor = 5238;
// tormentors
constexpr int MindTormentor = 5255;
constexpr int SoulTormentor = 5256;
constexpr int WaterTormentor = 5257;
constexpr int HeartTormentor = 5258;
constexpr int FleshTormentor = 5259;
constexpr int SpiritTormentor = 5260;
constexpr int EarthTormentor = 5261;
constexpr int SanityTormentor = 5263;
// titans
constexpr int MiseryTitan = 5246;
constexpr int RageTitan = 5247;
constexpr int DementiaTitan = 5248;
constexpr int AnguishTitan = 5249;
constexpr int DespairTitan = 5250;
constexpr int FuryTitan = 5251;
constexpr int RageTitan2 = 5252;
constexpr int DementiaTitan2 = 5253;
constexpr int DespairTitan2 = 5254;
constexpr int TorturewebDryder = 5266;
constexpr int GreaterDreamRider = 5267;
constexpr int GreaterDreamRider2 = 5268;
}
constexpr int ProphetVaresh = 5343;
constexpr int CommanderVaresh = 5344;
// Spirits
constexpr int InfuriatingHeat = 5766;
constexpr int Quicksand = 5769;
namespace PolymockSummon
{
enum
{
// Polymocks
MursaatElementalist = 5898,
FlameDjinn = 5899,
IceImp = 5900,
NagaShaman = 5901
};
}
constexpr int EbonVanguardAssassin = 5903;
namespace EotnDungeons
{
constexpr int DiscOfChaos = 6122;
constexpr int PlagueOfDestruction = 6134;
constexpr int ZhimMonns = 6297;
constexpr int Khabuus = 6299;
constexpr int DuncanTheBlack = 6456;
constexpr int JusticiarThommis = 6457;
constexpr int RandStormweaver = 6458;
constexpr int Selvetarm = 6459;
constexpr int Forgewright = 6460;
constexpr int HavokSoulwail = 6478;
constexpr int RragarManeater3 = 6609; // lvl 3
constexpr int RragarManeater12 = 6610; // lvl 1 and 2
constexpr int Arachni = 6844;
constexpr int Hidesplitter = 6852;
constexpr int PrismaticOoze = 6857;
constexpr int IlsundurLordofFire = 6865;
constexpr int EldritchEttin = 6872;
constexpr int TPSRegulartorGolem = 6883;
constexpr int MalfunctioningEnduringGolem = 6885;
constexpr int StormcloudIncubus = 6902;
constexpr int CyndrTheMountainHeart = 6965;
constexpr int InfernalSiegeWurm = 6978; // kath lvl1 boss
constexpr int Frostmaw = 6983;
constexpr int RemnantOfAntiquities = 6985;
constexpr int MurakaiLadyOfTheNight = 7059;
constexpr int ZoldarkTheUnholy = 7061;
constexpr int Brigand = 7060; // soo
constexpr int FendiNin = 7064;
constexpr int SoulOfFendiNin = 7065;
constexpr int KeymasterOfMurakai = 7069;
}
namespace BonusMissionPack
{
constexpr int WarAshenskull = 7130;
constexpr int RoxAshreign = 7131;
constexpr int AnrakTindershot = 7132;
constexpr int DettMortash = 7133;
constexpr int AkinCinderspire = 7134;
constexpr int TwangSootpaws = 7135;
constexpr int MagisEmberglow = 7136;
constexpr int MerciaTheSmug = 7165;
constexpr int OptimusCaliph = 7166;
constexpr int LazarusTheDire = 7167;
constexpr int AdmiralJakman = 7230;
constexpr int PalawaJoko = 7249;
constexpr int YuriTheHand = 7197;
constexpr int MasterRiyo = 7198;
constexpr int CaptainSunpu = 7200;
constexpr int MinisterWona = 7201;
}
namespace EotnDungeons
{
constexpr int AngrySnowman = 7462;
}
constexpr int Boo = 7500;
/* Birthday Minis */
/* unknowns are commented as a guess as inventory name but fit a pattern of id schema */
namespace Minipet
{
// More in-game drops and rewards
constexpr int MiniatureLegionnaire = 8035;
}
constexpr int LockedChest = 8192; // this is actually ->ExtraType
namespace Minipet
{
constexpr int MiniatureConfessorDorian = 8344;
constexpr int MiniaturePrincessSalma = 8349;
constexpr int MiniatureLivia = 8350;
constexpr int MiniatureEvennia = 8351;
constexpr int MiniatureConfessorIsaiah = 8352;
// missing miniature 8298 ?
constexpr int MiniaturePeacekeeperEnforcer = 8354;
constexpr int MiniatureMinisterReiko = 9038;
constexpr int MiniatureEcclesiateXunRao = 9039;
}
namespace SummoningStone
{
constexpr int ImperialCripplingSlash = 9043;
constexpr int ImperialTripleChop = 9044;
constexpr int ImperialBarrage = 9045;
constexpr int ImperialQuiveringBlade = 9046;
constexpr int TenguHundredBlades = 9047;
constexpr int TenguBroadHeadArrow = 9048;
constexpr int TenguPalmStrike = 9049;
constexpr int TenguLifeSheath = 9050;
constexpr int TenguAngchuElementalist = 9051;
constexpr int TenguFeveredDreams = 9052;
constexpr int TenguSpitefulSpirit = 9053;
constexpr int TenguPreservation = 9054;
constexpr int TenguPrimalRage = 9055;
constexpr int TenguGlassArrows = 9056;
constexpr int TenguWayOftheAssassin = 9057;
constexpr int TenguPeaceandHarmony = 9058;
constexpr int TenguSandstorm = 9059;
constexpr int TenguPanic = 9060;
constexpr int TenguAuraOftheLich = 9061;
constexpr int TenguDefiantWasXinrae = 9062;
}
}
}
}
+361
View File
@@ -0,0 +1,361 @@
// ReSharper disable CppUnusedIncludeDirective
#pragma once
#include "AgentIDs.h"
#include "ItemIDs.h"
#include "Maps.h"
#include "QuestIDs.h"
#include "Skills.h"
namespace GW {
namespace Constants {
enum class Campaign : uint32_t { Core, Prophecies, Factions, Nightfall, EyeOfTheNorth, BonusMissionPack };
enum class Difficulty { Normal, Hard };
enum class InstanceType { Outpost, Explorable, Loading };
enum class Profession : uint32_t {
None, Warrior, Ranger, Monk, Necromancer, Mesmer,
Elementalist, Assassin, Ritualist, Paragon, Dervish
};
enum class ProfessionByte : uint8_t {
None, Warrior, Ranger, Monk, Necromancer, Mesmer,
Elementalist, Assassin, Ritualist, Paragon, Dervish
};
static const char* GetProfessionAcronym(Profession prof) {
switch (prof) {
case GW::Constants::Profession::None: return "X";
case GW::Constants::Profession::Warrior: return "W";
case GW::Constants::Profession::Ranger: return "R";
case GW::Constants::Profession::Monk: return "Mo";
case GW::Constants::Profession::Necromancer: return "N";
case GW::Constants::Profession::Mesmer: return "Me";
case GW::Constants::Profession::Elementalist: return "E";
case GW::Constants::Profession::Assassin: return "A";
case GW::Constants::Profession::Ritualist: return "Rt";
case GW::Constants::Profession::Paragon: return "P";
case GW::Constants::Profession::Dervish: return "D";
default: return "";
}
}
static const wchar_t* GetWProfessionAcronym(Profession prof) {
switch (prof) {
case GW::Constants::Profession::None: return L"X";
case GW::Constants::Profession::Warrior: return L"W";
case GW::Constants::Profession::Ranger: return L"R";
case GW::Constants::Profession::Monk: return L"Mo";
case GW::Constants::Profession::Necromancer: return L"N";
case GW::Constants::Profession::Mesmer: return L"Me";
case GW::Constants::Profession::Elementalist: return L"E";
case GW::Constants::Profession::Assassin: return L"A";
case GW::Constants::Profession::Ritualist: return L"Rt";
case GW::Constants::Profession::Paragon: return L"P";
case GW::Constants::Profession::Dervish: return L"D";
default: return L"";
}
}
namespace Preference {
enum class CharSortOrder : uint32_t {
None, Alphabetize, PvPRP
};
}
enum class Attribute : uint32_t {
FastCasting, IllusionMagic, DominationMagic, InspirationMagic, // mesmer
BloodMagic, DeathMagic, SoulReaping, Curses, // necro
AirMagic, EarthMagic, FireMagic, WaterMagic, EnergyStorage, // ele
HealingPrayers, SmitingPrayers, ProtectionPrayers, DivineFavor, // monk
Strength, AxeMastery, HammerMastery, Swordsmanship, Tactics, // warrior
BeastMastery, Expertise, WildernessSurvival, Marksmanship, // ranger
DaggerMastery = 29, DeadlyArts, ShadowArts, // assassin (most)
Communing, RestorationMagic, ChannelingMagic, // ritualist (most)
CriticalStrikes, SpawningPower, // sin/rit primary (gw is weird)
SpearMastery, Command, Motivation, Leadership, // paragon
ScytheMastery, WindPrayers, EarthPrayers, Mysticism, // derv
None = 0xff
};
enum class AttributeByte : uint8_t {
FastCasting, IllusionMagic, DominationMagic, InspirationMagic, // mesmer
BloodMagic, DeathMagic, SoulReaping, Curses, // necro
AirMagic, EarthMagic, FireMagic, WaterMagic, EnergyStorage, // ele
HealingPrayers, SmitingPrayers, ProtectionPrayers, DivineFavor, // monk
Strength, AxeMastery, HammerMastery, Swordsmanship, Tactics, // warrior
BeastMastery, Expertise, WildernessSurvival, Marksmanship, // ranger
DaggerMastery = 29, DeadlyArts, ShadowArts, // assassin (most)
Communing, RestorationMagic, ChannelingMagic, // ritualist (most)
CriticalStrikes, SpawningPower, // sin/rit primary (gw is weird)
SpearMastery, Command, Motivation, Leadership, // paragon
ScytheMastery, WindPrayers, EarthPrayers, Mysticism, // derv
None = 0xff
};
enum class Bag : uint8_t {
None, Backpack, Belt_Pouch, Bag_1, Bag_2, Equipment_Pack,
Material_Storage, Unclaimed_Items, Storage_1, Storage_2,
Storage_3, Storage_4, Storage_5, Storage_6, Storage_7,
Storage_8, Storage_9, Storage_10, Storage_11, Storage_12,
Storage_13, Storage_14, Equipped_Items, Max
};
inline Bag& operator++(Bag& bag) {
if (bag == Bag::Max) return bag;
bag = static_cast<Bag>(static_cast<uint8_t>(bag) + 1);
return bag;
}
inline Bag operator++(Bag& bag, int) {
const Bag cpy = bag;
++bag;
return cpy;
}
// Order of storage panes.
enum class StoragePane : uint8_t {
Storage_1,Storage_2,Storage_3,Storage_4,Storage_5,
Storage_6,Storage_7,Storage_8,Storage_9,Storage_10,
Storage_11,Storage_12,Storage_13,Storage_14,Material_Storage
};
constexpr size_t BagMax = (size_t)Bag::Max;
enum class AgentType {
Living = 0xDB, Gadget = 0x200, Item = 0x400
};
enum class Allegiance : uint8_t {
Ally_NonAttackable = 0x1, Neutral = 0x2, Enemy = 0x3, Spirit_Pet = 0x4, Minion = 0x5, Npc_Minipet = 0x6
};
enum class ItemType : uint8_t {
Salvage, Axe = 2, Bag, Boots, Bow, Bundle, Chestpiece, Rune_Mod, Usable, Dye,
Materials_Zcoins, Offhand, Gloves, Hammer = 15, Headpiece, CC_Shards,
Key, Leggings, Gold_Coin, Quest_Item, Wand, Shield = 24, Staff = 26, Sword,
Kit = 29, Trophy, Scroll, Daggers, Present, Minipet, Scythe, Spear, Storybook = 43, Costume, Costume_Headpiece, Unknown = 0xff
};
enum HeroID : uint32_t {
NoHero, Norgu, Goren, Tahlkora, MasterOfWhispers, AcolyteJin, Koss,
Dunkoro, AcolyteSousuke, Melonni, ZhedShadowhoof, GeneralMorgahn,
MargridTheSly, Zenmai, Olias, Razah, MOX, KeiranThackeray, Jora,
PyreFierceshot, Anton, Livia, Hayda, Kahmu, Gwen, Xandra, Vekk,
Ogden, Merc1, Merc2, Merc3, Merc4, Merc5, Merc6, Merc7, Merc8,
Miku, ZeiRi, Devona, GhostofAlthea, Count
};
enum class MaterialSlot : uint32_t {
Bone, IronIngot, TannedHideSquare, Scale, ChitinFragment,
BoltofCloth, WoodPlank, GraniteSlab = 8,
PileofGlitteringDust, PlantFiber, Feather,
FurSquare, BoltofLinen, BoltofDamask, BoltofSilk,
GlobofEctoplasm, SteelIngot, DeldrimorSteelIngot,
MonstrousClaw, MonstrousEye, MonstrousFang, Ruby,
Sapphire, Diamond, OnyxGemstone, LumpofCharcoal,
ObsidianShard, TemperedGlassVial = 29, LeatherSquare,
ElonianLeatherSquare, VialofInk, RollofParchment,
RollofVellum, SpiritwoodPlank, AmberChunk, JadeiteShard,
BronzeZCoin, SilverZCoin, GoldZCoin,
Count
};
constexpr std::array HeroProfs = {
Profession::None,
Profession::Mesmer, // Norgu
Profession::Warrior,// Goren
Profession::Monk, // Tahlkora
Profession::Necromancer, // Master Of Whispers
Profession::Ranger, // Acolyte Jin
Profession::Warrior, // Koss
Profession::Monk, // Dunkoro
Profession::Elementalist, // Acolyte Sousuke
Profession::Dervish, // Melonni
Profession::Elementalist, // Zhed Shadowhoof
Profession::Paragon, // General Morgahn
Profession::Ranger, // Magrid The Sly
Profession::Assassin, // Zenmai
Profession::Necromancer, // Olias
Profession::None, // Razah
Profession::Dervish, // MOX
Profession::Paragon, // Keiran Thackeray
Profession::Warrior, // Jora
Profession::Ranger, // Pyre Fierceshot
Profession::Assassin, // Anton
Profession::Necromancer, // Livia
Profession::Paragon, // Hayda
Profession::Dervish, // Kahmu
Profession::Mesmer, // Gwen
Profession::Ritualist, // Xandra
Profession::Elementalist, // Vekk
Profession::Monk, // Ogden
Profession::None, // Mercenary Hero 1
Profession::None, // Mercenary Hero 2
Profession::None, // Mercenary Hero 3
Profession::None, // Mercenary Hero 4
Profession::None, // Mercenary Hero 5
Profession::None, // Mercenary Hero 6
Profession::None, // Mercenary Hero 7
Profession::None, // Mercenary Hero 8
Profession::Assassin, // Miku
Profession::Ritualist, // Zei Ri
Profession::Warrior, // Devona
Profession::Mesmer // Ghost of Althea
};
enum class TitleID : uint32_t {
Hero, TyrianCarto, CanthanCarto, Gladiator, Champion, Kurzick,
Luxon, Drunkard,
Deprecated_SkillHunter, // Pre hard mode update version
Survivor, KoaBD,
Deprecated_TreasureHunter, // Old title, non-account bound
Deprecated_Wisdom, // Old title, non-account bound
ProtectorTyria,
ProtectorCantha, Lucky, Unlucky, Sunspear, ElonianCarto,
ProtectorElona, Lightbringer, LDoA, Commander, Gamer,
SkillHunterTyria, VanquisherTyria, SkillHunterCantha,
VanquisherCantha, SkillHunterElona, VanquisherElona,
LegendaryCarto, LegendaryGuardian, LegendarySkillHunter,
LegendaryVanquisher, Sweets, GuardianTyria, GuardianCantha,
GuardianElona, Asuran, Deldrimor, Vanguard, Norn, MasterOfTheNorth,
Party, Zaishen, TreasureHunter, Wisdom, Codex,
None = 0xff
};
enum class Tick { NOT_READY, READY };
enum class InterfaceSize { SMALL = -1, NORMAL, LARGE, LARGER };
namespace HealthbarHeight {
constexpr size_t Small = 24;
constexpr size_t Normal = 22;
constexpr size_t Large = 26;
constexpr size_t Larger = 30;
}
// travel, region, districts
enum class ServerRegion {
International = -2,
America = 0,
Korea,
Europe,
China,
Japan,
Unknown = 0xff
};
// in-game language for maps and in-game text
enum class Language {
English,
Korean,
French,
German,
Italian,
Spanish,
TraditionalChinese,
Japanese = 8,
Polish,
Russian,
BorkBorkBork = 17,
Unknown = 0xff
};
enum class District { // arbitrary enum for game district
Current,
International,
American,
EuropeEnglish,
EuropeFrench,
EuropeGerman,
EuropeItalian,
EuropeSpanish,
EuropePolish,
EuropeRussian,
AsiaKorean,
AsiaChinese,
AsiaJapanese,
Unknown = 0xff
};
namespace Range {
constexpr float Touch = 144.f;
constexpr float Adjacent = 166.0f;
constexpr float Nearby = 252.0f;
constexpr float Area = 322.0f;
constexpr float Earshot = 1012.0f;
constexpr float Spellcast = 1248.0f;
constexpr float Spirit = 2512.0f;
constexpr float SpiritExtended = 3500.0f;
constexpr float Compass = 5000.0f;
};
namespace SqrRange {
constexpr float Adjacent = Range::Adjacent * Range::Adjacent;
constexpr float Nearby = Range::Nearby * Range::Nearby;
constexpr float Area = Range::Area * Range::Area;
constexpr float Earshot = Range::Earshot * Range::Earshot;
constexpr float Spellcast = Range::Spellcast * Range::Spellcast;
constexpr float Spirit = Range::Spirit * Range::Spirit;
constexpr float Compass = Range::Compass * Range::Compass;
};
namespace DialogID {
constexpr int UwTeleEnquire = 127; // "where can you teleport us to"
constexpr int UwTelePlanes = 139;
constexpr int UwTeleWastes = 140;
constexpr int UwTeleLab = 141;
constexpr int UwTeleMnt = 142;
constexpr int UwTelePits = 143;
constexpr int UwTelePools = 144;
constexpr int UwTeleVale = 145;
constexpr int FowCraftArmor = 127;
constexpr int FerryKamadanToDocks = 133; // Assistant Hahnna
constexpr int FerryDocksToKaineng = 136; // Mhenlo
constexpr int FerryDocksToLA = 137; // Mhenlo
constexpr int FerryGateToLA = 133; // Lionguard Neiro
// Profession Changer Dialogs.
constexpr int ProfChangeWarrior = 0x184;
constexpr int ProfChangeRanger = 0x284;
constexpr int ProfChangeMonk = 0x384;
constexpr int ProfChangeNecro = 0x484;
constexpr int ProfChangeMesmer = 0x584;
constexpr int ProfChangeEle = 0x684;
constexpr int ProfChangeAssassin = 0x784;
constexpr int ProfChangeRitualist = 0x884;
constexpr int ProfChangeParagon = 0x984;
constexpr int ProfChangeDervish = 0xA84;
constexpr int FactionMissionOutpost = 0x80000B;
constexpr int NightfallMissionOutpost = 0x85;
}
enum class EffectID : uint32_t {
black_cloud = 1,
mesmer_symbol = 4,
green_cloud = 7,
green_sparks = 8,
necro_symbol = 9,
ele_symbol = 11,
white_clouds = 13,
monk_symbol = 18,
bleeding = 23,
blind = 24,
burning = 25,
disease = 26,
poison = 27,
dazed = 28,
weakness = 29, //cracked_armor has same EffectID
assasin_symbol = 34,
ritualist_symbol = 35,
dervish_symbol = 36,
};
namespace Camera {
constexpr float FIRST_PERSON_DIST = 2.f;
constexpr float DEFAULT_DIST = 750.f;
}
constexpr size_t SkillMax = 0xd69;
}
}
+203
View File
@@ -0,0 +1,203 @@
#pragma once
namespace GW {
namespace Constants {
namespace ItemID { // aka item modelIDs
constexpr int Dye = 146;
// items that make agents
constexpr int GhostInTheBox = 6368;
constexpr int GhastlyStone = 32557;
constexpr int LegionnaireStone = 37810;
// materials
constexpr int Bone = 921;
constexpr int BoltofCloth = 925;
constexpr int PileofGlitteringDust = 929;
constexpr int Feather = 933;
constexpr int PlantFiber = 934;
constexpr int TannedHideSquare = 940;
constexpr int WoodPlank = 946;
constexpr int IronIngot = 948;
constexpr int Scale = 953;
constexpr int ChitinFragment = 954;
constexpr int GraniteSlab = 955;
constexpr int AmberChunk = 6532;
constexpr int BoltofDamask = 927;
constexpr int BoltofLinen = 926;
constexpr int BoltofSilk = 928;
constexpr int DeldrimorSteelIngot = 950;
constexpr int Diamond = 935;
constexpr int ElonianLeatherSquare = 943;
constexpr int FurSquare = 941;
constexpr int GlobofEctoplasm = 930;
constexpr int JadeiteShard = 6533;
constexpr int LeatherSquare = 942;
constexpr int LumpofCharcoal = 922;
constexpr int MonstrousClaw = 923;
constexpr int MonstrousEye = 931;
constexpr int MonstrousFang = 932;
constexpr int ObsidianShard = 945;
constexpr int OnyxGemstone = 936;
constexpr int RollofParchment = 951;
constexpr int RollofVellum = 952;
constexpr int Ruby = 937;
constexpr int Sapphire = 938;
constexpr int SpiritwoodPlank = 956;
constexpr int SteelIngot = 949;
constexpr int TemperedGlassVial = 939;
constexpr int VialofInk = 944;
// XP scrolls
constexpr int ScrollOfAdventurersInsight = 5853;
constexpr int ScrollOfBerserkersInsight = 5595;
constexpr int ScrollOfHerosInsight = 5594;
constexpr int ScrollOfHuntersInsight = 5976;
constexpr int ScrollOfRampagersInsight = 5975;
constexpr int ScrollOfSlayersInsight = 5611;
constexpr int ScrollOfTheLightbringer = 21233;
// pcons
constexpr int SkalefinSoup = 17061;
constexpr int PahnaiSalad = 17062;
constexpr int MandragorRootCake = 19170;
constexpr int Pies = 28436;
constexpr int Cupcakes = 22269;
constexpr int Apples = 28431;
constexpr int Corns = 28432;
constexpr int Eggs = 22752;
constexpr int Kabobs = 17060;
constexpr int Warsupplies = 35121;
constexpr int LunarPig = 29424;
constexpr int LunarRat = 29425;
constexpr int LunarOx = 29426;
constexpr int LunarTiger = 29427;
constexpr int LunarRabbit = 29428;
constexpr int LunarDragon = 29429;
constexpr int LunarSnake = 29430;
constexpr int LunarHorse = 29431;
constexpr int LunarSheep = 29432;
constexpr int LunarMonkey = 29433;
constexpr int LunarRooster = 29434;
constexpr int LunarDog = 29435;
constexpr int ConsEssence = 24859;
constexpr int ConsArmor = 24860;
constexpr int ConsGrail = 24861;
constexpr int ResScrolls = 26501;
constexpr int Mobstopper = 32558;
constexpr int Powerstone = 24862;
constexpr int Fruitcake = 21492;
constexpr int RedBeanCake = 15479;
constexpr int CremeBrulee = 15528;
constexpr int SugaryBlueDrink = 21812;
constexpr int ChocolateBunny = 22644;
constexpr int JarOfHoney = 31150;
constexpr int BRC = 31151;
constexpr int GRC = 31152;
constexpr int RRC = 31153;
constexpr int KrytalLokum = 35125;
constexpr int MinistreatOfPurity = 30208;
// level-1 alcohol
constexpr int Eggnog = 6375;
constexpr int DwarvenAle = 5585;
constexpr int HuntersAle = 910;
constexpr int Absinthe = 6367;
constexpr int WitchsBrew = 6049;
constexpr int Ricewine = 15477;
constexpr int ShamrockAle = 22190;
constexpr int Cider = 28435;
// level-5 alcohol
constexpr int Grog = 30855;
constexpr int SpikedEggnog = 6366;
constexpr int AgedDwarvenAle = 24593;
constexpr int AgedHuntersAle = 31145;
constexpr int Keg = 31146;
constexpr int FlaskOfFirewater = 2513;
constexpr int KrytanBrandy = 35124;
// DoA Gemstones
constexpr int MargoniteGem = 21128;
constexpr int StygianGem = 21129;
constexpr int TitanGem = 21130;
constexpr int TormentGem = 21131;
// Holiday Drops
constexpr int LunarToken = 21833;
constexpr int Lockpick = 22751;
constexpr int IdentificationKit = 2989;
constexpr int IdentificationKit_Superior = 5899;
constexpr int SalvageKit = 2992;
constexpr int SalvageKit_Expert = 2991;
constexpr int SalvageKit_Superior = 5900;
// Alcohol
constexpr int BattleIsleIcedTea = 36682;
constexpr int BottleOfJuniberryGin = 19172;
constexpr int BottleOfVabbianWine = 19173;
constexpr int ZehtukasJug = 19171;
// DP
constexpr int FourLeafClover = 22191; // party-wide
constexpr int OathOfPurity = 30206; // party-wide
constexpr int PeppermintCandyCane = 6370;
constexpr int RefinedJelly = 19039;
constexpr int ShiningBladeRations = 35127;
constexpr int WintergreenCandyCane = 21488;
// Morale
constexpr int ElixirOfValor = 21227; // party-wide
constexpr int Honeycomb = 26784; // party-wide
constexpr int PumpkinCookie = 28433;
constexpr int RainbowCandyCane = 21489; // party-wide
constexpr int SealOfTheDragonEmpire = 30211; // party-wide
// Summons
constexpr int GakiSummon = 30960;
constexpr int TurtleSummon = 30966;
// Summons x3
constexpr int TenguSummon = 30209;
constexpr int ImperialGuardSummon = 30210;
constexpr int WarhornSummon = 35126;
// Tonics
constexpr int ELGwen = 36442;
constexpr int ELMiku = 36451;
constexpr int ELMargo = 36456;
constexpr int ELZenmai = 36493;
// Other Consumables
constexpr int ArmbraceOfTruth = 21127;
constexpr int PhantomKey = 5882;
constexpr int ResScroll = 26501;
// Weapons
constexpr int DSR = 32823;
constexpr int EternalBlade = 1045;
constexpr int ObsidianEdge = 1900;
constexpr int VoltaicSpear = 2071;
constexpr int CrystallineSword = 399;
// Minis
constexpr int MiniDhuum = 32822;
// Bundles
constexpr int UnholyText = 2619;
constexpr int DungeonKey = 25410;
constexpr int BossKey = 25416;
constexpr int EnchantedTorch = 503;
constexpr int LightOfSeborhin = 15531;
constexpr int IstaniFireOil = 16339;
// Money
constexpr int GoldCoin = 2510;
constexpr int GoldCoins = 2511;
}
}
}
+898
View File
@@ -0,0 +1,898 @@
#pragma once
namespace GW {
namespace Constants {
enum class MapID : uint32_t {
None = 0,
Gladiators_Arena,
DEV_Test_Arena_1v1,
Test_map,
Warriors_Isle_outpost,
Hunters_Isle_outpost,
Wizards_Isle_outpost,
Warriors_Isle,
Hunters_Isle,
Wizards_Isle,
Bloodstone_Fen,
The_Wilds,
Aurora_Glade,
Diessa_Lowlands,
Gates_of_Kryta,
DAlessio_Seaboard,
Divinity_Coast,
Talmark_Wilderness,
The_Black_Curtain,
Sanctum_Cay,
Droknars_Forge_outpost,
The_Frost_Gate,
Ice_Caves_of_Sorrow,
Thunderhead_Keep,
Iron_Mines_of_Moladune,
Borlis_Pass,
Talus_Chute,
Griffons_Mouth,
The_Great_Northern_Wall,
Fort_Ranik,
Ruins_of_Surmia,
Xaquang_Skyway,
Nolani_Academy,
Old_Ascalon,
The_Fissure_of_Woe,
Ember_Light_Camp_outpost,
Grendich_Courthouse_outpost,
Glints_Challenge_mission,
Augury_Rock_outpost,
Sardelac_Sanitarium_outpost,
Piken_Square_outpost,
Sage_Lands,
Mamnoon_Lagoon,
Silverwood,
Ettins_Back,
Reed_Bog,
The_Falls,
Dry_Top,
Tangle_Root,
Henge_of_Denravi_outpost,
Test_Map_species_art, // "This is a debug map to allow testing of species art."
Senjis_Corner_outpost,
Burning_Isle_outpost,
Tears_of_the_Fallen,
Scoundrels_Rise,
Lions_Arch_outpost,
Cursed_Lands,
Bergen_Hot_Springs_outpost,
North_Kryta_Province,
Nebo_Terrace,
Majestys_Rest,
Twin_Serpent_Lakes,
Watchtower_Coast,
Stingray_Strand,
Kessex_Peak,
DAlessio_Arena_mission,
All_Call_Click_Point_1,
Burning_Isle,
Frozen_Isle,
Nomads_Isle,
Druids_Isle,
Isle_of_the_Dead_guild_hall,
The_Underworld,
Riverside_Province,
Tournament_6, // Shares description with HoH arena below
The_Hall_of_Heroes_arena_mission,
Broken_Tower_mission,
House_zu_Heltzer_outpost,
The_Courtyard_arena_mission,
Unholy_Temples_mission,
Burial_Mounds_mission,
Ascalon_City_outpost,
Tomb_of_the_Primeval_Kings,
The_Vault_mission,
The_Underworld_arena_mission,
Ascalon_Arena,
Sacred_Temples_mission,
Icedome,
Iron_Horse_Mine,
Anvil_Rock,
Lornars_Pass,
Snake_Dance,
Tascas_Demise,
Spearhead_Peak,
Ice_Floe,
Witmans_Folly,
Mineral_Springs,
Dreadnoughts_Drift,
Frozen_Forest,
Travelers_Vale,
Deldrimor_Bowl,
Regent_Valley,
The_Breach,
Ascalon_Foothills,
Pockmark_Flats,
Dragons_Gullet,
Flame_Temple_Corridor,
Eastern_Frontier,
The_Scar,
The_Amnoon_Oasis_outpost,
Diviners_Ascent,
Vulture_Drifts,
The_Arid_Sea,
Prophets_Path,
Salt_Flats,
Skyward_Reach,
Dunes_of_Despair,
Thirsty_River,
Elona_Reach,
Augury_Rock_mission,
The_Dragons_Lair,
Perdition_Rock,
Ring_of_Fire,
Abaddons_Mouth,
Hells_Precipice,
Titans_Tears,
Golden_Gates_mission,
Scarred_Earth2,
The_Eternal_Grove,
Lutgardis_Conservatory_outpost,
Vasburg_Armory_outpost,
Serenity_Temple_outpost,
Ice_Tooth_Cave_outpost,
Beacons_Perch_outpost,
Yaks_Bend_outpost,
Frontier_Gate_outpost,
Beetletun_outpost,
Fishermens_Haven_outpost,
Temple_of_the_Ages,
Ventaris_Refuge_outpost,
Druids_Overlook_outpost,
Maguuma_Stade_outpost,
Quarrel_Falls_outpost,
Ascalon_Academy_outpost, // not in live game
Gyala_Hatchery,
The_Catacombs,
Lakeside_County,
The_Northlands,
Ascalon_City_pre_searing, // ?
Ascalon_Academy_PvP_battle_mission_1,
Ascalon_Academy_PvP_battle_mission_2,
Ascalon_Academy_explorable,
Heroes_Audience_outpost,
Seekers_Passage_outpost,
Destinys_Gorge_outpost,
Camp_Rankor_outpost,
The_Granite_Citadel_outpost,
Marhans_Grotto_outpost,
Port_Sledge_outpost,
Copperhammer_Mines_outpost,
Green_Hills_County,
Wizards_Folly,
Regent_Valley_pre_Searing,
The_Barradin_Estate_outpost,
Ashford_Abbey_outpost,
Foibles_Fair_outpost,
Fort_Ranik_pre_Searing_outpost,
Burning_Isle_mission,
Druids_Isle_mission,
Frozen_Isle_mission = 170,
Warriors_Isle_mission,
Hunters_Isle_mission,
Wizards_Isle_mission,
Nomads_Isle_mission,
Isle_of_the_Dead_guild_hall_mission,
Frozen_Isle_outpost,
Nomads_Isle_outpost,
Druids_Isle_outpost,
Isle_of_the_Dead_guild_hall_outpost,
Fort_Koga_mission,
Shiverpeak_Arena,
Amnoon_Arena_mission,
Deldrimor_Arena_mission,
The_Crag_mission,
The_Underworld_gvg_mission, // UW guild hall not in live game
The_Underworld_guild_hall,
The_Underworld_guild_hall_preview,
Random_Arenas_outpost,
Team_Arenas_outpost,
Sorrows_Furnace,
Grenths_Footprint,
All_Call_Click_Point_2,
Cavalon_outpost,
Kaineng_Center_outpost,
Drazach_Thicket,
Jaya_Bluffs,
Shenzun_Tunnels,
Archipelagos,
Maishang_Hills,
Mount_Qinkai,
Melandrus_Hope,
Rheas_Crater,
Silent_Surf,
Unwaking_Waters_mission,
Morostav_Trail,
Deldrimor_War_Camp_outpost,
Dragons_Thieves,
Heroes_Crypt_mission,
Mourning_Veil_Falls,
Ferndale,
Pongmei_Valley,
Monastery_Overlook1,
Zen_Daijun_outpost_mission,
Minister_Chos_Estate_outpost_mission,
Vizunah_Square_mission,
Nahpui_Quarter_outpost_mission,
Tahnnakai_Temple_outpost_mission,
Arborstone_outpost_mission,
Boreas_Seabed_outpost_mission,
Sunjiang_District_outpost_mission,
Fort_Aspenwood_mission,
The_Eternal_Grove_outpost_mission,
The_Jade_Quarry_mission,
Gyala_Hatchery_outpost_mission,
Raisu_Palace_outpost_mission,
Imperial_Sanctum_outpost_mission,
Unwaking_Waters,
Grenz_Frontier_mission,
The_Ancestral_Lands_mission,
Amatz_Basin,
Kaanai_Canyon_mission,
Shadows_Passage,
Raisu_Palace,
The_Aurios_Mines,
Panjiang_Peninsula,
Kinya_Province,
Haiju_Lagoon,
Sunqua_Vale,
Wajjun_Bazaar,
Bukdek_Byway,
The_Undercity,
Shing_Jea_Monastery_outpost,
Shing_Jea_Arena,
Arborstone_explorable,
Minister_Chos_Estate_explorable,
Zen_Daijun_explorable,
Boreas_Seabed_explorable,
Great_Temple_of_Balthazar_outpost,
Tsumei_Village_outpost,
Seitung_Harbor_outpost,
Ran_Musu_Gardens_outpost,
Linnok_Courtyard,
Dwayna_Vs_Grenth,
Dwaynas_Camp,
Grenths_Camp,
Sunjiang_District_explorable,
Minister_Chos_Estate_cinematic,
Zen_Daijun_cinematic,
The_Jade_Quarry_Kurzick_cinematic,
Nahpui_Quarter_cinematic,
Tahnnaka_Temple_cinematic,
Arborstone_cinematic,
Boreas_Seabed_cinematic,
Sunjiang_District_cinematic,
Nahpui_Quarter_explorable,
Urgozs_Warren,
The_Eternal_Grove_cinematic,
Gyala_Hatchery_cinematic,
Tahnnakai_Temple_explorable,
Raisu_Palace_cinematic,
Imperial_Sanctum_cinematic,
Altrumm_Ruins,
Zos_Shivros_Channel,
Dragons_Throat,
Isle_of_Weeping_Stone_outpost,
Isle_of_Jade_outpost,
Harvest_Temple_outpost,
Breaker_Hollow_outpost,
Leviathan_Pits_outpost,
Isle_of_the_Nameless,
Zaishen_Challenge_outpost,
Zaishen_Elite_outpost,
Maatu_Keep_outpost,
Zin_Ku_Corridor_outpost,
Monastery_Overlook2,
Brauer_Academy_outpost,
Durheim_Archives_outpost,
Bai_Paasu_Reach_outpost,
Seafarers_Rest_outpost,
Bejunkan_Pier,
Vizunah_Square_Local_Quarter_outpost,
Vizunah_Square_Foreign_Quarter_outpost,
Fort_Aspenwood_Luxon_outpost,
Fort_Aspenwood_Kurzick_outpost,
The_Jade_Quarry_Luxon_outpost,
The_Jade_Quarry_Kurzick_outpost,
Unwaking_Waters_Luxon_outpost,
Unwaking_Waters_Kurzick_outpost,
Saltspray_Beach_mission,
Etnaran_Keys_mission,
Raisu_Pavilion,
Kaineng_Docks,
The_Marketplace_outpost,
Vizunah_Square_Local_Quarter_cinematic,
Vizunah_Square_Foreign_Quarter_cinematic,
The_Jade_Quarry_Luxon_cinematic,
The_Deep,
Ascalon_Arena_mission,
Annihilation_mission,
Kill_Count_Training_mission,
Priest_Annihilation_Training,
Obelisk_Annihilation_Training_mission,
Saoshang_Trail,
Shiverpeak_Arena_mission,
// The boat map icon for cross-campaign travel. NF one is later
Travel_Battle_Isles,
Travel_Tyria,
Travel_Cantha,
// Zaishen battles
DAlessio_Arena_mission3 = 318,
Amnoon_Arena_mission3,
Fort_Koga_mission3,
Heroes_Crypt_mission3,
Shiverpeak_Arena_mission3,
Fort_Aspenwood_Kurzick_cinematic,
Fort_Aspenwood_Luxon_cinematic,
The_Harvest_Ceremony_Kurzick_cinematic, // same cinematic but different teleport destination
The_Harvest_Ceremony_Luxon_cinematic, // same cinematic but different teleport destination
Imperial_Sanctum_explorable,
Saltspray_Beach_Luxon_outpost,
Saltspray_Beach_Kurzick_outpost,
Heroes_Ascent_outpost,
Grenz_Frontier_Luxon_outpost,
Grenz_Frontier_Kurzick_outpost,
The_Ancestral_Lands_Luxon_outpost,
The_Ancestral_Lands_Kurzick_outpost,
Etnaran_Keys_Luxon_outpost,
Etnaran_Keys_Kurzick_outpost,
Kaanai_Canyon_Luxon_outpost,
Kaanai_Canyon_Kurzick_outpost,
DAlessio_Arena_mission2,
Amnoon_Arena_mission2,
Fort_Koga_mission2,
Heroes_Crypt_mission2,
Shiverpeak_Arena_mission2,
The_Hall_of_Heroes,
The_Courtyard,
Scarred_Earth,
The_Underworld_PvP,
Tanglewood_Copse_outpost,
Saint_Anjekas_Shrine_outpost,
Eredon_Terrace_outpost,
Divine_Path,
Brawlers_Pit_mission,
Petrified_Arena_mission,
Seabed_Arena_mission,
Isle_of_Weeping_Stone_mission,
Isle_of_Jade_mission,
Imperial_Isle_mission,
Isle_of_Meditation_mission,
Imperial_Isle_outpost,
Isle_of_Meditation_outpost,
Isle_of_Weeping_Stone,
Isle_of_Jade,
Imperial_Isle,
Isle_of_Meditation,
Random_Arenas_Test,
Shing_Jea_Arena_mission,
All_Skills, // "This is a debug map which forces download of all skill effects"
Dragon_Arena,
Jahai_Bluffs,
Kamadan_mission,
Marga_Coast,
Fahranur_mission,
Sunward_Marches,
Vortex_Elona, // Vortex travel icon on the Elona world map
Barbarous_Shore,
Camp_Hojanu_outpost,
Bahdok_Caverns,
Wehhan_Terraces_outpost,
Dejarin_Estate,
Arkjok_Ward,
Yohlon_Haven_outpost,
Gandara_the_Moon_Fortress,
Vortex_Realm_of_Torment, // Vortex travel icon on the Realm of Torment world map
The_Floodplain_of_Mahnkelon,
Lions_Arch_Sunspears_in_Kryta,
Turais_Procession,
Sunspear_Sanctuary_outpost,
Aspenwood_Gate_Kurzick_outpost,
Aspenwood_Gate_Luxon_outpost,
Jade_Flats_Kurzick_outpost,
Jade_Flats_Luxon_outpost,
Yatendi_Canyons,
Chantry_of_Secrets_outpost,
Garden_of_Seborhin,
Holdings_of_Chokhin,
Mihanu_Township_outpost,
Vehjin_Mines,
Basalt_Grotto_outpost,
Forum_Highlands,
Kaineng_Center_Sunspears_in_Cantha,
Sebelkeh_Basilica, // Not in live game
Resplendent_Makuun,
Honur_Hill_outpost,
Wilderness_of_Bahdza,
Sun_Docks_cinematic, // Arriving in Elona?
Vehtendi_Valley,
Yahnur_Market_outpost,
// 408-412 are explorable maps all called "Here Be Dragons"
The_Hidden_City_of_Ahdashim = 413,
The_Kodash_Bazaar_outpost,
Lions_Gate,
Monastery_Overlook_cinematic, // Factions opening cutscene
Bejunkan_Pier_cinematic,
Lions_Gate_cinematic,
The_Mirror_of_Lyss,
Secure_the_Refuge,
Venta_Cemetery,
Kamadan_Jewel_of_Istan_explorable,
The_Tribunal,
Kodonur_Crossroads,
Rilohn_Refuge,
Pogahn_Passage,
Moddok_Crevice,
Tihark_Orchard,
Consulate,
Plains_of_Jarin,
Sunspear_Great_Hall_outpost,
Cliffs_of_Dohjok,
Dzagonur_Bastion,
Dasha_Vestibule,
Grand_Court_of_Sebelkeh,
Command_Post,
Jokos_Domain,
Bone_Palace_outpost,
The_Ruptured_Heart,
The_Mouth_of_Torment_outpost,
The_Shattered_Ravines,
Lair_of_the_Forgotten_outpost,
Poisoned_Outcrops,
The_Sulfurous_Wastes,
The_Ebony_Citadel_of_Mallyx_mission,
The_Alkali_Pan,
A_Land_of_Heroes,
Crystal_Overlook,
Kamadan_Jewel_of_Istan_outpost,
Gate_of_Torment_outpost,
Gate_of_Anguish_elite_mission,
Secure_the_Refuge_cinematic,
Evacuation_cinematic,
Test_Map_solo_areas, // "This is a debug map to allow multiplayer testing of current solo areas."
Nightfallen_Garden,
Churrhir_Fields,
Beknur_Harbor_outpost,
Kodonur_Crossroads_cinematic,
Rilohn_Refuge_cinematic,
Pogahn_Passage_cinematic,
The_Underworld2,
Heart_of_Abaddon,
The_Underworld3,
Nightfallen_Coast,
Nightfallen_Jahai,
Depths_of_Madness,
Rollerbeetle_Racing,
Domain_of_Fear,
Gate_of_Fear_outpost,
Domain_of_Pain,
Bloodstone_Fen_quest,
Domain_of_Secrets,
Gate_of_Secrets_outpost,
Domain_of_Anguish,
Ooze_Pit_mission,
Jennurs_Horde,
Nundu_Bay,
Gate_of_Desolation,
Champions_Dawn_outpost,
Ruins_of_Morah,
Fahranur_The_First_City,
Bjora_Marches,
Zehlon_Reach,
Lahtenda_Bog,
Arbor_Bay,
Issnur_Isles,
Beknur_Harbor,
Mehtani_Keys,
Kodlonu_Hamlet_outpost,
Island_of_Shehkah,
Jokanur_Diggings,
Blacktide_Den,
Consulate_Docks,
Gate_of_Pain,
Gate_of_Madness,
Abaddons_Gate,
Sunspear_Arena,
Travel_Elona, // Boat travel icon in Elona
Ice_Cliff_Chasms,
Bokka_Amphitheatre,
Riven_Earth,
The_Astralarium_outpost,
Throne_of_Secrets,
Churranu_Island_Arena_mission,
Shing_Jea_Monastery_mission,
Haiju_Lagoon_mission,
Jaya_Bluffs_mission,
Seitung_Harbor_mission,
Tsumei_Village_mission,
Seitung_Harbor_mission_2,
Tsumei_Village_mission_2,
Minister_Chos_Estate_mission_2,
Drakkar_Lake,
Island_of_Shehkah_cinematic, // Nightfall opening cutscene
Jokanur_Diggings_cinematic,
Blacktide_Den_cinematic,
Consulate_Docks_cinematic,
Tihark_Orchard_cinematic,
Dzagonur_Bastion_cinematic,
Hidden_City_of_Ahdashim_cinematic,
Grand_Court_of_Sebelkeh_cinematic,
Jennurs_Horde_cinematic,
Nundu_Bay_cinematic,
Gates_of_Desolation_cinematic,
Ruins_of_Morah_cinematic,
Domain_of_Pain_cinematic,
Gate_of_Madness_cinematic,
Abaddons_Gate_cinematic,
Uncharted_Isle_outpost,
Isle_of_Wurms_outpost,
Uncharted_Isle,
Isle_of_Wurms,
Uncharted_Isle_mission,
Isle_of_Wurms_mission,
Ahmtur_Arena_mission, // Not in live game
Sunspear_Arena_mission,
Corrupted_Isle_outpost,
Isle_of_Solitude_outpost,
Corrupted_Isle,
Isle_of_Solitude,
Corrupted_Isle_mission,
Isle_of_Solitude_mission,
Sun_Docks,
Chahbek_Village,
Remains_of_Sahlahja,
Jaga_Moraine,
Bombardment_mission,
Norrhart_Domains,
Hero_Battles_outpost,
The_Beachhead_mission,
The_Crossing_mission,
Desert_Sands_mission,
Varajar_Fells,
Dajkah_Inlet,
The_Shadow_Nexus,
Chahbek_Village_cutscene,
Throne_Of_Secrets_outpost, // Not in live game
Sparkfly_Swamp,
Gate_of_the_Nightfallen_Lands_outpost,
Cathedral_of_Flames_Level_1,
The_Troubled_Keeper,
// Landmarks on the Elona world map
Fortress_of_Jahai,
Halls_of_Chokhin,
Citadel_of_Dzagon,
Dynastic_Tombs,
Verdant_Cascades,
Cathedral_of_Flames_Level_2,
Cathedral_of_Flames_Level_3,
Magus_Stones,
Catacombs_of_Kathandrax_Level_1,
Catacombs_of_Kathandrax_Level_2,
Alcazia_Tangle,
Rragars_Menagerie_Level_1,
Rragars_Menagerie_Level_2,
Rragars_Menagerie_Level_3,
Ooze_Pit,
Slavers_Exile_Level_1,
Oolas_Lab_Level_1,
Oolas_Lab_Level_2,
Oolas_Lab_Level_3,
Shards_of_Orr_Level_1,
Shards_of_Orr_Level_2,
Shards_of_Orr_Level_3,
Arachnis_Haunt_Level_1,
Arachnis_Haunt_Level_2,
// 586-591 are all dungeon maps called "TEMP"
Five_Team_Test = 592, // "5 team test"
Fetid_River_mission,
Overlook_mission, // Heroes Ascent unused/prototype map
Cemetery_mission, // Heroes Ascent unused/prototype map
Forgotten_Shrines_mission,
Track_mission, // Heroes Ascent unused/prototype map
Antechamber_mission,
Collision_mission, // Heroes Ascent unused/prototype map
The_Hall_of_Heroes2, // Actual HA hall of heroes? Unused?
// 601-603 are all dungeon maps called "TEMP"
Vloxen_Excavations_Level_1 = 604,
Vloxen_Excavations_Level_2,
Vloxen_Excavations_Level_3,
Heart_of_the_Shiverpeaks_Level_1,
Heart_of_the_Shiverpeaks_Level_2,
Heart_of_the_Shiverpeaks_Level_3,
// 610,611 are dungeon maps with no name
Bloodstone_Caves_Level_1 = 612,
Bloodstone_Caves_Level_2,
Bloodstone_Caves_Level_3,
Bogroot_Growths_Level_1,
Bogroot_Growths_Level_2,
Ravens_Point_Level_1,
Ravens_Point_Level_2,
Ravens_Point_Level_3,
Slavers_Exile_Level_2,
Slavers_Exile_Level_3,
Slavers_Exile_Level_4,
Slavers_Exile_Level_5,
Vloxs_Falls,
Battledepths_Level_1,
Battledepths_Level_2,
Battledepths_Level_3,
Sepulchre_of_Dragrimmar_Level_1,
Sepulchre_of_Dragrimmar_Level_2,
Frostmaws_Burrows_Level_1,
Frostmaws_Burrows_Level_2,
Frostmaws_Burrows_Level_3,
Frostmaws_Burrows_Level_4,
Frostmaws_Burrows_Level_5,
Darkrime_Delves_Level_1,
Darkrime_Delves_Level_2,
Darkrime_Delves_Level_3,
Gadds_Encampment_outpost,
Umbral_Grotto_outpost,
Rata_Sum_outpost,
Tarnished_Haven_outpost,
Eye_of_the_North_outpost,
Sifhalla_outpost,
Gunnars_Hold_outpost,
Olafstead_outpost,
Hall_of_Monuments,
Dalada_Uplands,
Doomlore_Shrine_outpost,
Grothmar_Wardowns,
Longeyes_Ledge_outpost,
Sacnoth_Valley,
Central_Transfer_Chamber_outpost,
Curse_of_the_Nornbear,
Blood_Washes_Blood,
A_Gate_Too_Far_Level_1,
A_Gate_Too_Far_Level_2,
A_Gate_Too_Far_Level_3,
The_Elusive_Golemancer_Level_1,
The_Elusive_Golemancer_Level_2,
The_Elusive_Golemancer_Level_3,
Finding_the_Bloodstone_Level_1,
Finding_the_Bloodstone_Level_2,
Finding_the_Bloodstone_Level_3,
Genius_Operated_Living_Enchanted_Manifestation,
Against_the_Charr,
Warband_of_Brothers_Level_1,
Warband_of_Brothers_Level_2,
Warband_of_Brothers_Level_3,
Assault_on_the_Stronghold,
Destructions_Depths_Level_1,
Destructions_Depths_Level_2,
Destructions_Depths_Level_3,
A_Time_for_Heroes,
Warband_Training,
Boreal_Station_outpost,
Catacombs_of_Kathandrax_Level_3,
Hall_of_Primordus, // Unused explorable area
Attack_of_the_Nornbear,
Cinematic_Cave_Norn_Cursed,
Cinematic_Steppe_Interrogation,
Cinematic_Interior_Research,
Cinematic_Eye_Vision_A,
Cinematic_Eye_Vision_B,
Cinematic_Eye_Vision_C,
Cinematic_Eye_Vision_D,
Polymock_Coliseum,
Polymock_Glacier,
Polymock_Crossing,
Cinematic_Mountain_Resolution,
Cold_as_Ice,
Beneath_Lions_Arch,
Tunnels_Below_Cantha,
Caverns_Below_Kamadan,
Cinematic_Mountain_Dwarfs,
Service_In_Defense_of_the_Eye,
Mano_a_Norn_o,
Service_Practice_Dummy,
Hero_Tutorial,
Prototype_Map,
The_Norn_Fighting_Tournament = 700,
Secret_Lair_of_the_Snowmen,
Norn_Brawling_Championship,
Kilroys_Punchout_Training,
Fronis_Irontoes_Lair_mission,
The_Justiciars_End,
Designer_Test_Map,
The_Great_Norn_Alemoot,
Varajar_Fells_Bear_Club_quest,
The_Crossing_mission2, // Unused RA arena?
Epilogue,
Insidious_Remnants,
The_Beachhead_mission2, // Unused RA arena?
Bombardment_mission2, // Unused RA arena?
Desert_Sands_mission2, // Unused RA arena?
MISSION_CINEMATIC_MISSION_PACK_TYRIA_INTRODUCTION,
The_Battlefield_cinematic,
Attack_on_Jaliss_Camp,
The_Hierophants_Stronghold_cinematic,
The_Asura_Plan_cinematic,
Creature_Test_Map,
Costume_Brawl_outpost,
Whitefury_Rapids_mission,
Kysten_Shore_mission,
Deepway_Ruins_mission,
Plikkup_Works_mission,
Kilroys_Punchout_Tournament,
Special_Ops_Flame_Temple_Corridor,
Special_Ops_Dragons_Gullet,
Special_Ops_Grendich_Courthouse,
// Cinematics replayed at the Scrying Pool in Hall of Monuments
Encounter_in_the_Depths_hom_cinematic,
Into_the_North_hom_cinematic,
Arrival_at_the_Eye_hom_cinematic,
Joras_Curse_hom_cinematic,
The_Nornbear_hom_cinematic,
Blood_Washes_Blood_hom_cinematic,
Joras_Redemption_hom_cinematic,
Sign_of_the_Raven_hom_cinematic,
Olaf_and_Ogden_hom_cinematic,
Audience_with_the_King_hom_cinematic,
The_Battlefield_hom_cinematic,
The_Charr_Prisoner_hom_cinematic,
The_Warband_hom_cinematic,
Questions_and_Answers_hom_cinematic,
The_Hierophants_Stronghold_hom_cinematic,
Revolution_hom_cinematic,
The_Asura_Plan_hom_cinematic,
Bookah_hom_cinematic,
Oola_hom_cinematic,
Gadd_hom_cinematic,
At_the_Bloodstone_hom_cinematic,
Before_the_Battle_hom_cinematic,
Price_of_Victory_hom_cinematic,
The_Great_Dwarf_hom_cinematic,
The_Great_Destroyer_hom_cinematic,
Ogdens_Benediction_hom_cinematic,
// EotN map travel icons
Asura_Gate_Tyria,
Asura_Gate_Cantha,
Asura_Gate_Elona,
// "Mission" maps, used for icon status on world map
Finding_the_Bloodstone_mission,
Genius_Operated_Living_Enchanted_Manifestation_mission,
Against_the_Charr_mission,
Warband_of_brothers_mission,
Assault_on_the_Stronghold_mission,
Destructions_Depths_mission,
A_Time_for_Heroes_mission,
Curse_of_the_Nornbear_mission,
Blood_Washes_Blood_mission,
A_Gate_Too_Far_mission,
The_Elusive_Golemancer_mission,
The_Tengu_Accords,
The_Battle_of_Jahai,
The_Flight_North,
The_Rise_of_the_White_Mantle,
Battle_Isles_Marketplace, // Xunlai Marketplace?
MISSION_CINEMATIC_MISSION_PACK_CANTHA_INTRODUCTION,
Mission_Pack_Test,
MISSION_CINEMATIC_MISSION_PACK_ELONA_INTRODUCTION,
MISSION_CINEMATIC_MISSION_PACK_GWX_INTRODUCTION,
Piken_Square_pre_Searing_outpost,
// Map 780 has invalid continent, name and description
Secret_Lair_of_the_Snowmen2 = 781, // ?
Secret_Lair_of_the_Snowmen3, // ?
Droknars_Forge_cinematic, // ?
Isle_of_the_Nameless_PvP,
Rollerbeetle_Racing_HeroBattles,
Dragon_Arena_gvg,
Dwayna_vs_Grenth_gvg,
Temple_of_the_Ages_ROX,
Wajjun_Bazaar_POX,
Bokka_Amphitheatre_NOX,
Secret_Underground_Lair,
Golem_Tutorial_Simulation,
Snowball_Dominance,
Zaishen_Menagerie_Grounds,
Zaishen_Menagerie_outpost,
Codex_Arena_outpost,
// Maps 797-805 are dungeon maps with the name "..."
The_Underworld_Something_Wicked_This_Way_Comes = 806,
The_Underworld_Dont_Fear_the_Reapers,
Lions_Arch_Halloween_outpost,
Lions_Arch_Wintersday_outpost,
Lions_Arch_Canthan_New_Year_outpost,
Ascalon_City_Wintersday_outpost,
Droknars_Forge_Halloween_outpost,
Droknars_Forge_Wintersday_outpost,
Tomb_of_the_Primeval_Kings_Halloween_outpost,
Shing_Jea_Monastery_Dragon_Festival_outpost,
Shing_Jea_Monastery_Canthan_New_Year_outpost,
Kaineng_Center_Canthan_New_Year_outpost,
Kamadan_Jewel_of_Istan_Halloween_outpost,
Kamadan_Jewel_of_Istan_Wintersday_outpost,
Kamadan_Jewel_of_Istan_Canthan_New_Year_outpost,
Eye_of_the_North_outpost_Wintersday_outpost,
Tester_HUB,
DAlessio_Arena_mission4,
Amnoon_Arena_mission4,
Churranu_Island_Arena_mission2,
Fort_Koga_mission4,
Petrified_Arena_mission2,
Heroes_Crypt_mission4,
Seabed_Arena_mission2,
Deldrimor_Arena_mission2,
Brawlers_Pit_mission2,
The_Crag_mission2,
Sunspear_Arena_mission2,
Shing_Jea_Arena_mission2,
Ascalon_Arena_mission2,
Shiverpeak_Arena_mission4,
War_in_Kryta_Talmark_Wilderness,
War_in_Kryta_Trial_of_Zinn,
War_in_Kryta_Divinity_Coast,
War_in_Kryta_Lions_Arch_Keep,
War_in_Kryta_DAlessio_Seaboard,
War_in_Kryta_The_Battle_for_Lions_Arch,
War_in_Kryta_Riverside_Province,
War_in_Kryta_Lions_Arch,
War_in_Kryta_The_Mausoleum,
War_in_Kryta_Rise,
War_in_Kryta_Shadows_in_the_Jungle,
War_in_Kryta_A_Vengeance_of_Blades,
War_in_Kryta_Auspicious_Beginnings,
Beetletun_explorable, // Is this part of War in Kryta? or Rise?
// War in Kryta? Festival event quests?
Aurora_Glade2, // explorable
Majestys_Rest2,
Watchtower_Coast2,
Olafstead_cinematic,
The_Great_Snowball_Fight_of_the_Gods__Operation_Crush_Spirits,
The_Great_Snowball_Fight_of_the_Gods__Fighting_in_a_Winter_Wonderland,
Embark_Beach,
Regent_Valley_1059_AE,
Lakeside_County_1059_AE,
Dragons_Throat_area__What_Waits_in_Shadow = 860,
Kaineng_Center_Winds_of_Change__A_Chance_Encounter,
The_Marketplace_area__Tracking_the_Corruption,
Bukdek_Byway_Winds_of_Change__Cantha_Courier_Crisis,
Tsumei_Village_Winds_of_Change__A_Treatys_a_Treaty,
Seitung_Harbor_area__Deadly_Cargo,
Tahnnakai_Temple_Winds_of_Change__The_Rescue_Attempt,
Wajjun_Bazaar_Winds_of_Change__Violence_in_the_Streets,
Scarred_Psyche_mission,
Shadows_Passage_Winds_of_Change__Calling_All_Thugs,
Altrumm_Ruins__Finding_Jinnai,
Shing_Jea_Monastery__Raid_on_Shing_Jea_Monastery,
Kaineng_Center_Winds_of_Change__Raid_on_Kaineng_Center,
Wajjun_Bazaar_Winds_of_Change__Ministry_of_Oppression,
The_Final_Confrontation,
Lakeside_County_1070_AE,
Ashford_Catacombs_1070_AE,
Count = 0x370,
};
}
}
+273
View File
@@ -0,0 +1,273 @@
#pragma once
namespace GW {
namespace Constants {
enum class QuestID : uint32_t {
None = 0,
// Underworld
UW_Chamber = 101,
UW_Wastes,
UW_UWG,
UW_Mnt,
UW_Pits,
UW_Planes,
UW_Pools,
UW_Escort,
UW_Restore,
UW_Vale,
// Fissure of woe
Fow_Defend = 202,
Fow_ArmyOfDarknesses,
Fow_WailingLord,
Fow_Griffons,
Fow_Slaves,
Fow_Restore,
Fow_Hunt,
Fow_Forgemaster,
Fow_Tos = 211,
Fow_Toc,
Fow_Khobay = 224,
//Doa
Doa_DeathbringerCompany = 749,
Doa_RiftBetweenUs = 752,
Doa_ToTheRescue = 753,
// city
Doa_City = 751,
// Veil
Doa_BreachingStygianVeil = 742,
Doa_BroodWars = 755,
// Foundry
Doa_FoundryBreakout = 743,
Doa_FoundryOfFailedCreations = 744,
// slavers
The_Last_Hierophant = 917,
// Prophecies
ZaishenMission_The_Great_Northern_Wall = 936,
ZaishenMission_Fort_Ranik = 937,
ZaishenMission_Ruins_of_Surmia = 938,
ZaishenMission_Nolani_Academy = 939,
ZaishenMission_Borlis_Pass = 940,
ZaishenMission_The_Frost_Gate = 941,
ZaishenMission_Gates_of_Kryta = 942,
ZaishenMission_DAlessio_Seaboard = 943,
ZaishenMission_Divinity_Coast = 944,
ZaishenMission_The_Wilds = 945,
ZaishenMission_Bloodstone_Fen = 946,
ZaishenMission_Aurora_Glade = 947,
ZaishenMission_Riverside_Province = 948,
ZaishenMission_Sanctum_Cay = 949,
ZaishenMission_Dunes_of_Despair = 950,
ZaishenMission_Thirsty_River = 951,
ZaishenMission_Elona_Reach = 952,
ZaishenMission_Augury_Rock = 953,
ZaishenMission_The_Dragons_Lair = 954,
ZaishenMission_Ice_Caves_of_Sorrow = 955,
ZaishenMission_Iron_Mines_of_Moladune = 956,
ZaishenMission_Thunderhead_Keep = 957,
ZaishenMission_Ring_of_Fire = 958,
ZaishenMission_Abaddons_Mouth = 959,
ZaishenMission_Hells_Precipice = 960,
// Factions
ZaishenMission_Minister_Chos_Estate = 1119,
ZaishenMission_Zen_Daijun = 961,
ZaishenMission_Vizunah_Square = 962,
ZaishenMission_Nahpui_Quarter = 963,
ZaishenMission_Tahnnakai_Temple = 964,
ZaishenMission_Arborstone = 965,
ZaishenMission_Boreas_Seabed = 966,
ZaishenMission_Sunjiang_District = 967,
ZaishenMission_The_Eternal_Grove = 968,
ZaishenMission_Gyala_Hatchery = 970,
ZaishenMission_Unwaking_Waters = 969,
ZaishenMission_Raisu_Palace = 971,
ZaishenMission_Imperial_Sanctum = 972,
// Nightfall
ZaishenMission_Chahbek_Village = 978,
ZaishenMission_Jokanur_Diggings = 979,
ZaishenMission_Blacktide_Den = 980,
ZaishenMission_Consulate_Docks = 981,
ZaishenMission_Venta_Cemetery = 982,
ZaishenMission_Kodonur_Crossroads = 983,
ZaishenMission_Pogahn_Passage = 1181,
ZaishenMission_Rilohn_Refuge = 984,
ZaishenMission_Moddok_Crevice = 985,
ZaishenMission_Tihark_Orchard = 986,
ZaishenMission_Dasha_Vestibule = 988,
ZaishenMission_Dzagonur_Bastion = 987,
ZaishenMission_Grand_Court_of_Sebelkeh = 989,
ZaishenMission_Jennurs_Horde = 990,
ZaishenMission_Nundu_Bay = 991,
ZaishenMission_Gate_of_Desolation = 992,
ZaishenMission_Ruins_of_Morah = 993,
ZaishenMission_Gate_of_Pain = 994,
ZaishenMission_Gate_of_Madness = 995,
ZaishenMission_Abaddons_Gate = 996,
// Eye of the North
ZaishenMission_Finding_the_Bloodstone = 1000,
ZaishenMission_The_Elusive_Golemancer = 1001,
ZaishenMission_G_O_L_E_M = 1002,
ZaishenMission_Against_the_Charr = 1003,
ZaishenMission_Warband_of_Brothers = 1004,
ZaishenMission_Assault_on_the_Stronghold = 1005,
ZaishenMission_Curse_of_the_Nornbear = 1006,
ZaishenMission_A_Gate_Too_Far = 1008,
ZaishenMission_Blood_Washes_Blood = 1007,
ZaishenMission_Destructions_Depths = 1009,
ZaishenMission_A_Time_for_Heroes = 1010,
ZaishenBounty_Urgoz = 1025,
ZaishenBounty_Ilsundur_Lord_of_Fire = 1048,
ZaishenBounty_Chung_The_Attuned = 1026,
ZaishenBounty_Mungri_Magicbox = 1029,
ZaishenBounty_The_Stygian_Lords = 1046,
ZaishenBounty_Rragar_Maneater = 1049,
ZaishenBounty_Murakai_Lady_of_the_Night = 1050,
ZaishenBounty_Prismatic_Ooze = 1051,
ZaishenBounty_Havok_Soulwail = 1052,
ZaishenBounty_Frostmaw_the_Kinslayer = 1053,
ZaishenBounty_Remnant_of_Antiquities = 1054,
ZaishenBounty_Plague_of_Destruction = 1055,
ZaishenBounty_Zoldark_the_Unholy = 1056,
ZaishenBounty_Khabuus = 1057,
ZaishenBounty_Zhim_Monns = 1058,
ZaishenBounty_Eldritch_Ettin = 1059,
ZaishenBounty_Fendi_Nin = 1060,
ZaishenBounty_TPS_Regulator_Golem = 1061,
ZaishenBounty_Arachni = 1062,
ZaishenBounty_Forgewight = 1063,
ZaishenBounty_Selvetarm = 1064,
ZaishenBounty_Justiciar_Thommis = 1065,
ZaishenBounty_Rand_Stormweaver = 1066,
ZaishenBounty_Duncan_the_Black = 1067,
ZaishenBounty_Fronis_Irontoe = 1068,
ZaishenBounty_Magmus = 1070,
ZaishenBounty_Lord_Khobay = 1086,
ZaishenVanquish_Dejarin_Estate = 1201,
ZaishenVanquish_Watchtower_Coast = 1202,
ZaishenVanquish_Arbor_Bay = 1203,
ZaishenVanquish_Barbarous_Shore = 1204,
ZaishenVanquish_Deldrimor_Bowl = 1205,
ZaishenVanquish_Boreas_Seabed = 1206,
ZaishenVanquish_Cliffs_of_Dohjok = 1207,
ZaishenVanquish_Diessa_Lowlands = 1208,
ZaishenVanquish_Bukdek_Byway = 1209,
ZaishenVanquish_Bjora_Marches = 1210,
ZaishenVanquish_Crystal_Overlook = 1211,
ZaishenVanquish_Diviners_Ascent = 1212,
ZaishenVanquish_Dalada_Uplands = 1213,
ZaishenVanquish_Drazach_Thicket = 1214,
ZaishenVanquish_Fahranur_the_First_City = 1215,
ZaishenVanquish_Dragons_Gullet = 1216,
ZaishenVanquish_Ferndale = 1217,
ZaishenVanquish_Forum_Highlands = 1218,
ZaishenVanquish_Dreadnoughts_Drift = 1219,
ZaishenVanquish_Drakkar_Lake = 1220,
ZaishenVanquish_Dry_Top = 1221,
ZaishenVanquish_Tears_of_the_Fallen = 1222,
ZaishenVanquish_Gyala_Hatchery = 1223,
ZaishenVanquish_Ettins_Back = 1224,
ZaishenVanquish_Gandara_the_Moon_Fortress = 1225,
ZaishenVanquish_Grothmar_Wardowns = 1226,
ZaishenVanquish_Flame_Temple_Corridor = 1227,
ZaishenVanquish_Haiju_Lagoon = 1228,
ZaishenVanquish_Frozen_Forest = 1229,
ZaishenVanquish_Garden_of_Seborhin = 1230,
ZaishenVanquish_Grenths_Footprint = 1231,
ZaishenVanquish_Jaya_Bluffs = 1232,
ZaishenVanquish_Holdings_of_Chokhin = 1233,
ZaishenVanquish_Ice_Cliff_Chasms = 1234,
ZaishenVanquish_Griffons_Mouth = 1235,
ZaishenVanquish_Kinya_Province = 1236,
ZaishenVanquish_Issnur_Isles = 1237,
ZaishenVanquish_Jaga_Moraine = 1238,
ZaishenVanquish_Ice_Floe = 1239,
ZaishenVanquish_Maishang_Hills = 1240,
ZaishenVanquish_Jahai_Bluffs = 1241,
ZaishenVanquish_Riven_Earth = 1242,
ZaishenVanquish_Icedome = 1243,
ZaishenVanquish_Minister_Chos_Estate = 1244,
ZaishenVanquish_Mehtani_Keys = 1245,
ZaishenVanquish_Sacnoth_Valley = 1246,
ZaishenVanquish_Iron_Horse_Mine = 1247,
ZaishenVanquish_Morostav_Trail = 1248,
ZaishenVanquish_Plains_of_Jarin = 1249,
ZaishenVanquish_Sparkfly_Swamp = 1250,
ZaishenVanquish_Kessex_Peak = 1251,
ZaishenVanquish_Mourning_Veil_Falls = 1252,
ZaishenVanquish_The_Alkali_Pan = 1253,
ZaishenVanquish_Varajar_Fells = 1254,
ZaishenVanquish_Lornars_Pass = 1255,
ZaishenVanquish_Pongmei_Valley = 1256,
ZaishenVanquish_The_Floodplain_of_Mahnkelon = 1257,
ZaishenVanquish_Verdant_Cascades = 1258,
ZaishenVanquish_Majestys_Rest = 1259,
ZaishenVanquish_Raisu_Palace = 1260,
ZaishenVanquish_The_Hidden_City_of_Ahdashim = 1261,
ZaishenVanquish_Rheas_Crater = 1262,
ZaishenVanquish_Mamnoon_Lagoon = 1263,
ZaishenVanquish_Shadows_Passage = 1264,
ZaishenVanquish_The_Mirror_of_Lyss = 1265,
ZaishenVanquish_Saoshang_Trail = 1266,
ZaishenVanquish_Nebo_Terrace = 1267,
ZaishenVanquish_Shenzun_Tunnels = 1268,
ZaishenVanquish_The_Ruptured_Heart = 1269,
ZaishenVanquish_Salt_Flats = 1270,
ZaishenVanquish_North_Kryta_Province = 1271,
ZaishenVanquish_Silent_Surf = 1272,
ZaishenVanquish_The_Shattered_Ravines = 1273,
ZaishenVanquish_Scoundrels_Rise = 1274,
ZaishenVanquish_Old_Ascalon = 1275,
ZaishenVanquish_Sunjiang_District = 1276,
ZaishenVanquish_The_Sulphurous_Wastes = 1277,
ZaishenVanquish_Magus_Stones = 1278,
ZaishenVanquish_Perdition_Rock = 1279,
ZaishenVanquish_Sunqua_Vale = 1280,
ZaishenVanquish_Turais_Procession = 1281,
ZaishenVanquish_Norrhart_Domains = 1282,
ZaishenVanquish_Pockmark_Flats = 1283,
ZaishenVanquish_Tahnnakai_Temple = 1284,
ZaishenVanquish_Vehjin_Mines = 1285,
ZaishenVanquish_Poisoned_Outcrops = 1286,
ZaishenVanquish_Prophets_Path = 1287,
ZaishenVanquish_The_Eternal_Grove = 1288,
ZaishenVanquish_Tascas_Demise = 1289,
ZaishenVanquish_Respendent_Makuun = 1290,
ZaishenVanquish_Reed_Bog = 1291,
ZaishenVanquish_Unwaking_Waters = 1292,
ZaishenVanquish_Stingray_Strand = 1293,
ZaishenVanquish_Sunward_Marches = 1294,
ZaishenVanquish_Regent_Valley = 1295,
ZaishenVanquish_Wajjun_Bazaar = 1296,
ZaishenVanquish_Yatendi_Canyons = 1297,
ZaishenVanquish_Twin_Serpent_Lakes = 1298,
ZaishenVanquish_Sage_Lands = 1299,
ZaishenVanquish_Xaquang_Skyway = 1300,
ZaishenVanquish_Zehlon_Reach = 1301,
ZaishenVanquish_Tangle_Root = 1302,
ZaishenVanquish_Silverwood = 1303,
ZaishenVanquish_Zen_Daijun = 1304,
ZaishenVanquish_The_Arid_Sea = 1305,
ZaishenVanquish_Nahpui_Quarter = 1306,
ZaishenVanquish_Skyward_Reach = 1307,
ZaishenVanquish_The_Scar = 1308,
ZaishenVanquish_The_Black_Curtain = 1309,
ZaishenVanquish_Panjiang_Peninsula = 1310,
ZaishenVanquish_Snake_Dance = 1311,
ZaishenVanquish_Travelers_Vale = 1312,
ZaishenVanquish_The_Breach = 1313,
ZaishenVanquish_Lahtenda_Bog = 1314,
ZaishenVanquish_Spearhead_Peak = 1315
};
}
}
File diff suppressed because it is too large Load Diff
+968
View File
@@ -0,0 +1,968 @@
namespace GW {
enum class CallTargetType : uint32_t;
enum class WorldActionId : uint32_t;
struct GamePos;
struct Effect;
struct Vec2f;
typedef uint32_t AgentID;
namespace Merchant {
enum class TransactionType : uint32_t;
}
namespace Constants {
enum class Language;
enum class MapID : uint32_t;
enum class InstanceType;
enum class QuestID : uint32_t;
enum class SkillID : uint32_t;
}
namespace Chat {
enum Channel : int;
typedef uint32_t Color;
}
namespace Chat {
enum Channel : int;
}
namespace SkillbarMgr {
struct SkillTemplate;
}
namespace UI {
struct WindowPosition;
enum class FlagPreference : uint32_t;
enum class NumberPreference : uint32_t;
enum class StringPreference : uint32_t;
enum class EnumPreference : uint32_t;
struct CompassPoint;
enum class UIMessage : uint32_t {
kNone = 0x0,
kFrameMessage_0x1,
kFrameMessage_0x2,
kFrameMessage_0x3,
kFrameMessage_0x4,
kFrameMessage_0x5,
kFrameMessage_0x6,
kFrameMessage_0x7,
kResize, // 0x8
kInitFrame, // 0x9
kFrameMessage_0xa,
kDestroyFrame, // 0xb
kFrameDisabledChange, // wparam = is_enabled
kFrameMessage_0xd,
kFrameMessage_0xe,
kFrameMessage_0xf,
kFrameMessage_0x10,
kFrameMessage_0x11,
kFrameMessage_0x12,
kFrameMessage_0x13,
kFrameMessage_0x14,
kFrameMessage_0x15,
kFrameMessage_0x16,
kFrameMessage_0x17,
kFrameMessage_0x18,
kFrameMessage_0x19,
kFrameMessage_0x1a,
kFrameMessage_0x1b,
kFrameMessage_0x1c,
kFrameMessage_0x1d,
kKeyDown = 0x20, // wparam = UIPacket::kKeyAction* - Updated from 0x1e to 0x20
kSetFocus, // 0x1f, wparam = 1 or 0
kKeyUp, // 0x20, wparam = UIPacket::kKeyAction*
kFrameMessage_0x21, // 0x21
kMouseClick, // 0x22, wparam = UIPacket::kMouseClick*
kFrameMessage_0x23, // 0x23
kMouseCoordsClick, // 0x24, wparam = UIPacket::kMouseCoordsClick*
kFrameMessage_0x25, // 0x25
kMouseUp, // 0x26, wparam = UIPacket::kMouseCoordsClick*
kFrameMessage_0x27, // 0x27
kFrameMessage_0x28, // 0x28
kFrameMessage_0x29, // 0x29
kFrameMessage_0x2a, // 0x2a
kFrameMessage_0x2b, // 0x2b
kToggleButtonDown, // 0x2c
kFrameMessage_0x2d, // 0x2d
kMouseClick2 = 0x31, // 0x2e, wparam = UIPacket::kMouseAction*
kMouseAction, // 0x2f, wparam = UIPacket::kMouseAction*
kRenderFrame_0x30, // 0x30
kRenderFrame_0x31 = 0x35, // 0x31
kFrameVisibilityChanged, // 0x32, wparam = is_visible
kSetLayout, // 0x33
kMeasureContent, // 0x34
kFrameMessage_0x35, // 0x35
kFrameMessage_0x36, // 0x36
kRefreshContent, // 0x37
kFrameMessage_0x38,
kFrameMessage_0x39,
kFrameMessage_0x3a,
kFrameMessage_0x3b,
kFrameMessage_0x3c = 0x44,
kFrameMessage_0x3d,
kFrameMessage_0x3e,
kFrameMessage_0x3f,
kFrameMessage_0x40,
kFrameMessage_0x41,
kFrameMessage_0x42,
kRenderFrame_0x43, // 0x43
kFrameMessage_0x44 = 0x52,
kFrameMessage_0x45,
kFrameMessage_0x46 = 0x56,
kFrameMessage_0x47, // 0x47, Multiple uses depending on frame
kFrameMessage_0x48, // 0x48, Multiple uses depending on frame
kFrameMessage_0x49, // 0x49, Multiple uses depending on frame
kFrameMessage_0x4a, // 0x4a, Multiple uses depending on frame
kFrameMessage_0x4b, // 0x4b, Multiple uses depending on frame
kFrameMessage_0x4c, // 0x4c, Multiple uses depending on frame
kFrameMessage_0x4d, // 0x4d, Multiple uses depending on frame
kFrameMessage_0x4e, // 0x4e, Multiple uses depending on frame
kFrameMessage_0x4f, // 0x4f, Multiple uses depending on frame
kFrameMessage_0x50, // 0x50, Multiple uses depending on frame
kFrameMessage_0x51, // 0x51, Multiple uses depending on frame
kFrameMessage_0x52, // 0x52, Multiple uses depending on frame
kFrameMessage_0x53, // 0x53, Multiple uses depending on frame
kFrameMessage_0x54, // 0x54, Multiple uses depending on frame
kFrameMessage_0x55, // 0x55, Multiple uses depending on frame
kFrameMessage_0x56, // 0x56, Multiple uses depending on frame
kFrameMessage_0x57, // 0x57, Multiple uses depending on frame
// High bit messages start at 0x10000000
kMessage_0x10000000 = 0x10000000,
kMessage_0x10000001,
kMessage_0x10000002,
kMessage_0x10000003,
kMessage_0x10000004,
kMessage_0x10000005,
kMessage_0x10000006,
kAgentUpdate, // 0x10000007, wparam = uint32_t agent_id
kAgentDestroy, // 0x10000008, wparam = uint32_t agent_id
kUpdateAgentEffects, // 0x10000009
kMessage_0x1000000a,
kMessage_0x1000000b,
kDialogueMessage, // 0x1000000c
kMessage_0x1000000d,
kMessage_0x1000000e,
kMessage_0x1000000f,
kMessage_0x10000010,
kMessage_0x10000011,
kMessage_0x10000012,
kMessage_0x10000013,
kMessage_0x10000014,
kMessage_0x10000015,
kMessage_0x10000016,
kAgentSpeechBubble, // 0x10000017
kMessage_0x10000018,
kShowAgentNameTag, // 0x10000019, wparam = AgentNameTagInfo*
kHideAgentNameTag, // 0x1000001A
kSetAgentNameTagAttribs, // 0x1000001B, wparam = AgentNameTagInfo*
kMessage_0x1000001c,
kSetAgentProfession, // 0x1000001D, wparam = UIPacket::kSetAgentProfession*
kMessage_0x1000001e,
kMessage_0x1000001f,
kChangeTarget, // 0x10000020, wparam = UIPacket::kChangeTarget*
kMessage_0x10000021,
kMessage_0x10000022,
kMessage_0x10000023,
kAgentSkillActivated, // 0x10000024, kAgentSkillPacket
kAgentSkillActivatedInstantly, // 0x10000025, kAgentSkillPacket
kAgentSkillCancelled, // 0x10000026, kAgentSkillPacket
kAgentSkillStartedCast, // 0x10000027, wparam = UIPacket::kAgentStartCasting*
kMessage_0x10000028,
kShowMapEntryMessage, // 0x10000029, wparam = { wchar_t* title, wchar_t* subtitle }
kSetCurrentPlayerData, // 0x1000002A, fired after setting the worldcontext player name
kMessage_0x1000002b,
kMessage_0x1000002c,
kMessage_0x1000002d,
kMessage_0x1000002e,
kMessage_0x1000002f,
kMessage_0x10000030,
kMessage_0x10000031,
kMessage_0x10000032,
kMessage_0x10000033,
kPostProcessingEffect, // 0x10000034, Triggered when drunk. wparam = UIPacket::kPostProcessingEffect
kMessage_0x10000035,
kMessage_0x10000036,
kMessage_0x10000037,
kHeroAgentAdded, // 0x10000038, hero assigned to agent/inventory/ai mode
kHeroDataAdded, // 0x10000039, hero info received from server (name, level etc)
kMessage_0x1000003a,
kMessage_0x1000003b,
kMessage_0x1000003c,
kMessage_0x1000003d,
kMessage_0x1000003e,
kMessage_0x1000003f,
kShowXunlaiChest, // 0x10000040
kMessage_0x10000041,
kMessage_0x10000042,
kMessage_0x10000043,
kMessage_0x10000044,
kMessage_0x10000045,
kMinionCountUpdated, // 0x10000046
kMoraleChange, // 0x10000047, wparam = {agent id, morale percent }
kMessage_0x10000048,
kMessage_0x10000049,
kMessage_0x1000004a,
kMessage_0x1000004b,
kMessage_0x1000004c,
kMessage_0x1000004d,
kMessage_0x1000004e,
kMessage_0x1000004f,
kLoginStateChanged, // 0x10000050, wparam = {bool is_logged_in, bool unk }
kMessage_0x10000051,
kMessage_0x10000052,
kMessage_0x10000053,
kMessage_0x10000054,
kEffectAdd, // 0x10000055, wparam = {agent_id, GW::Effect*}
kEffectRenew, // 0x10000056, wparam = GW::Effect*
kEffectRemove, // 0x10000057, wparam = effect id
kMessage_0x10000058,
kMessage_0x10000059,
kMessage_0x1000005a,
kSkillActivated, // 0x1000005b, wparam ={ uint32_t agent_id , uint32_t skill_id }
kMessage_0x1000005c,
kMessage_0x1000005d,
kUpdateSkillbar, // 0x1000005E, wparam ={ uint32_t agent_id , ... }
kUpdateSkillsAvailable, // 0x1000005f, Triggered on a skill unlock, profession change or map load
kMessage_0x10000060,
kMessage_0x10000061,
kMessage_0x10000062,
kMessage_0x10000063,
kPlayerTitleChanged, // 0x10000064, wparam = { uint32_t player_id, uint32_t title_id }
kTitleProgressUpdated, // 0x10000065, wparam = title_id
kExperienceGained, // 0x10000066, wparam = experience amount
kMessage_0x10000067,
kMessage_0x10000068,
kMessage_0x10000069,
kMessage_0x1000006a,
kMessage_0x1000006b,
kMessage_0x1000006c,
kMessage_0x1000006d,
kMessage_0x1000006e,
kMessage_0x1000006f,
kMessage_0x10000070,
kMessage_0x10000071,
kMessage_0x10000072,
kMessage_0x10000073,
kMessage_0x10000074,
kMessage_0x10000075,
kMessage_0x10000076,
kMessage_0x10000077,
kMessage_0x10000078,
kMessage_0x10000079,
kMessage_0x1000007a,
kMessage_0x1000007b,
kMessage_0x1000007c,
kMessage_0x1000007d,
kMessage_0x1000007e,
kWriteToChatLog, // 0x1000007F, wparam = UIPacket::kWriteToChatLog*
kWriteToChatLogWithSender, // 0x10000080, wparam = UIPacket::kWriteToChatLogWithSender*
kAllyOrGuildMessage, // 0x10000081, wparam = UIPacket::kAllyOrGuildMessage*
kPlayerChatMessage, // 0x10000082, wparam = UIPacket::kPlayerChatMessage*
kMessage_0x10000083,
kFloatingWindowMoved, // 0x10000084, wparam = frame_id
kMessage_0x10000085,
kMessage_0x10000086,
kMessage_0x10000087,
kMessage_0x10000088,
kMessage_0x10000089,
kMessage_0x1000008a,
kFriendUpdated, // 0x1000008B, wparam = { GW::Friend*, ... }
kMapLoaded, // 0x1000008C
kMessage_0x1000008d,
kMessage_0x1000008e,
kMessage_0x1000008f,
kMessage_0x10000090,
kMessage_0x10000091,
kOpenWhisper, // 0x10000092, wparam = wchar* name
kMessage_0x10000093,
kMessage_0x10000094,
kMessage_0x10000095,
kMessage_0x10000096,
kMessage_0x10000097,
kLoadMapContext, // 0x10000098, wparam = UIPacket::kLoadMapContext
kMessage_0x10000099,
kMessage_0x1000009a,
kMessage_0x1000009b,
kDialogueMessageUpdated, // 0x1000009c
kLogout, // 0x1000009D, wparam = { bool unknown, bool character_select }
kCompassDraw, // 0x1000009E, wparam = UIPacket::kCompassDraw*
kMessage_0x1000009f,
kMessage_0x100000a0,
kMessage_0x100000a1,
kOnScreenMessage, // 0x100000A2, wparam = wchar_** encoded_string
kDialogButton, // 0x100000A3, wparam = DialogButtonInfo*
kMessage_0x100000a4,
kMessage_0x100000a5,
kDialogBody, // 0x100000A6, wparam = DialogBodyInfo*
kMessage_0x100000a7,
kMessage_0x100000a8,
kMessage_0x100000a9,
kMessage_0x100000aa,
kMessage_0x100000ab,
kMessage_0x100000ac,
kMessage_0x100000ad,
kMessage_0x100000ae,
kMessage_0x100000af,
kMessage_0x100000b0,
kMessage_0x100000b1,
kMessage_0x100000b2,
kTargetNPCPartyMember, // 0x100000B3, wparam = { uint32_t unk, uint32_t agent_id }
kTargetPlayerPartyMember, // 0x100000B4, wparam = { uint32_t unk, uint32_t player_number }
kVendorWindow, // 0x100000B5, wparam = UIPacket::kVendorWindow
kMessage_0x100000b6,
kMessage_0x100000b7,
kMessage_0x100000b8,
kVendorItems, // 0x100000B9, wparam = UIPacket::kVendorItems
kMessage_0x100000ba,
kVendorTransComplete, // 0x100000BB, wparam = *TransactionType
kMessage_0x100000bc,
kVendorQuote, // 0x100000BD, wparam = UIPacket::kVendorQuote
kMessage_0x100000be,
kMessage_0x100000bf,
kMessage_0x100000c0,
kMessage_0x100000c1,
kStartMapLoad, // 0x100000C2, wparam = { uint32_t map_id, ...}
kMessage_0x100000c3,
kMessage_0x100000c4,
kMessage_0x100000c5,
kMessage_0x100000c6,
kWorldMapUpdated, // 0x100000C7, Triggered when an area in the world map has been discovered/updated
kMessage_0x100000c8,
kMessage_0x100000c9,
kMessage_0x100000ca,
kMessage_0x100000cb,
kMessage_0x100000cc,
kMessage_0x100000cd,
kMessage_0x100000ce,
kMessage_0x100000cf,
kMessage_0x100000d0,
kMessage_0x100000d1,
kMessage_0x100000d2,
kMessage_0x100000d3,
kMessage_0x100000d4,
kMessage_0x100000d5,
kMessage_0x100000d6,
kMessage_0x100000d7,
kMessage_0x100000d8,
kMessage_0x100000d9,
kGuildMemberUpdated, // 0x100000DA, wparam = { GuildPlayer::name_ptr }
kMessage_0x100000db,
kMessage_0x100000dc,
kMessage_0x100000dd,
kMessage_0x100000de,
kMessage_0x100000df,
kMessage_0x100000e0,
kShowHint, // 0x100000E1, wparam = { uint32_t icon_type, wchar_t* message_enc }
kMessage_0x100000e2,
kMessage_0x100000e3,
kMessage_0x100000e4,
kMessage_0x100000e5,
kMessage_0x100000e6,
kMessage_0x100000e7,
kMessage_0x100000e8,
kWeaponSetSwapComplete, // 0x100000E9, wparam = UIPacket::kWeaponSwap*
kWeaponSetSwapCancel, // 0x100000EA
kWeaponSetUpdated, // 0x100000EB
kUpdateGoldCharacter, // 0x100000EC, wparam = { uint32_t unk, uint32_t gold_character }
kUpdateGoldStorage, // 0x100000ED, wparam = { uint32_t unk, uint32_t gold_storage }
kInventorySlotUpdated, // 0x100000EE, Triggered when an item is moved into a slot
kEquipmentSlotUpdated, // 0x100000EF, Triggered when an item is moved into a slot
kMessage_0x100000f0,
kInventorySlotCleared, // 0x100000F1, Triggered when an item has been removed from a slot
kEquipmentSlotCleared, // 0x100000F2, Triggered when an item has been removed from a slot
kMessage_0x100000f3,
kMessage_0x100000f4,
kMessage_0x100000f5,
kMessage_0x100000f6,
kMessage_0x100000f7,
kMessage_0x100000f8,
kMessage_0x100000f9,
kPvPWindowContent, // 0x100000FA
kMessage_0x100000fb,
kMessage_0x100000fc,
kMessage_0x100000fd,
kMessage_0x100000fe,
kMessage_0x100000ff,
kMessage_0x10000100,
kMessage_0x10000101,
kPreStartSalvage, // 0x10000102, { uint32_t item_id, uint32_t kit_id }
kTomeSkillSelection, // 0x10000103, wparam = UIPacket::kTomeSkillSelection*
kMessage_0x10000104,
kTradePlayerUpdated, // 0x10000105, wparam = GW::TraderPlayer*
kItemUpdated, // 0x10000106, wparam = UIPacket::kItemUpdated*
kMessage_0x10000107,
kMessage_0x10000108,
kMessage_0x10000109,
kMessage_0x1000010a,
kMessage_0x1000010b,
kMessage_0x1000010c,
kMessage_0x1000010d,
kMessage_0x1000010e,
kMessage_0x1000010f,
kMessage_0x10000110,
kMapChange, // 0x10000111, wparam = map id
kMessage_0x10000112,
kMessage_0x10000113,
kMessage_0x10000114,
kCalledTargetChange, // 0x10000115, wparam = { player_number, target_id }
kMessage_0x10000116,
kMessage_0x10000117,
kMessage_0x10000118,
kErrorMessage, // 0x10000119, wparam = { int error_index, wchar_t* error_encoded_string }
kPartyHardModeChanged, // 0x1000011A, wparam = { int is_hard_mode }
kPartyAddHenchman, // 0x1000011B
kPartyRemoveHenchman, // 0x1000011C
kMessage_0x1000011d,
kPartyAddHero, // 0x1000011E
kPartyRemoveHero, // 0x1000011F
kMessage_0x10000120,
kMessage_0x10000121,
kMessage_0x10000122,
kMessage_0x10000123,
kPartyAddPlayer, // 0x10000124
kMessage_0x10000125,
kPartyRemovePlayer, // 0x10000126
kMessage_0x10000127,
kMessage_0x10000128,
kMessage_0x10000129,
kDisableEnterMissionBtn, // 0x1000012A, wparam = boolean (1 = disabled, 0 = enabled)
kMessage_0x1000012b,
kMessage_0x1000012c,
kShowCancelEnterMissionBtn, // 0x1000012D
kMessage_0x1000012e,
kPartyDefeated, // 0x1000012F
kMessage_0x10000130,
kMessage_0x10000131,
kMessage_0x10000132,
kPartySearchCreated, // 0x10000133, wparam = GW::PartySearch*
kPartySearchIdChanged, // 0x10000134, wparam = uint32_t* party_search_id
kPartySearchRemoved, // 0x10000135, wparam = uint32_t* party_search_id
kPartySearchUpdated, // 0x10000136, wparam = GW::PartySearch*
kPartySearchInviteReceived, // 0x10000137, wparam = UIPacket::kPartySearchInviteReceived*
kMessage_0x10000138,
kPartySearchInviteSent, // 0x10000139
kPartyShowConfirmDialog, // 0x1000013A, wparam = UIPacket::kPartyShowConfirmDialog
kMessage_0x1000013b,
kMessage_0x1000013c,
kMessage_0x1000013d,
kMessage_0x1000013e,
kMessage_0x1000013f,
kPreferenceEnumChanged, // 0x10000140, wparam = UiPacket::kPreferenceEnumChanged
kPreferenceFlagChanged, // 0x10000141, wparam = UiPacket::kPreferenceFlagChanged
kPreferenceValueChanged, // 0x10000142, wparam = UiPacket::kPreferenceValueChanged
kUIPositionChanged, // 0x10000143, wparam = UIPacket::kUIPositionChanged
kPreBuildLoginScene, // 0x10000144, Called with no args right before login scene is drawn
kMessage_0x10000145,
kMessage_0x10000146,
kMessage_0x10000147,
kMessage_0x10000148,
kMessage_0x10000149,
kMessage_0x1000014a,
kMessage_0x1000014b,
kMessage_0x1000014c,
kMessage_0x1000014d,
kQuestAdded, // 0x1000014E, wparam = { quest_id, ... }
kQuestDetailsChanged, // 0x1000014F, wparam = { quest_id, ... }
kQuestRemoved, // 0x10000150, wparam = { quest_id, ... }
kClientActiveQuestChanged, // 0x10000151, wparam = { quest_id, ... }. Triggered when the game requests the current quest to change
kMessage_0x10000152,
kServerActiveQuestChanged, // 0x10000153, wparam = UIPacket::kServerActiveQuestChanged*. Triggered when the server requests the current quest to change
kUnknownQuestRelated, // 0x10000154
kMessage_0x10000155,
kDungeonComplete, // 0x10000156
kMissionComplete, // 0x10000157
kMessage_0x10000158,
kVanquishComplete, // 0x10000159
kObjectiveAdd, // 0x1000015A, wparam = UIPacket::kObjectiveAdd*
kObjectiveComplete, // 0x1000015B, wparam = UIPacket::kObjectiveComplete*
kObjectiveUpdated, // 0x1000015C, wparam = UIPacket::kObjectiveUpdated*
kMessage_0x1000015d,
kMessage_0x1000015e,
kMessage_0x1000015f,
kMessage_0x10000160,
kMessage_0x10000161,
kMessage_0x10000162,
kMessage_0x10000163,
kMessage_0x10000164,
kTradeSessionStart, // 0x10000165, wparam = { trade_state, player_number }
kMessage_0x10000166,
kMessage_0x10000167,
kMessage_0x10000168,
kMessage_0x10000169,
kMessage_0x1000016a,
kTradeSessionUpdated, // 0x1000016b, no args
kMessage_0x1000016c,
kMessage_0x1000016d,
kMessage_0x1000016e,
kMessage_0x1000016f,
kMessage_0x10000170,
kMessage_0x10000171,
kMessage_0x10000172,
kMessage_0x10000173,
kMessage_0x10000174,
kCheckUIState, // 0x10000175
kMessage_0x10000176,
kMessage_0x10000177,
kMessage_0x10000178,
kMessage_0x10000178_1, // added to GW 2026-02-26
kMessage_0x10000178_2, // added to GW 2026-02-26
kMessage_0x10000178_3, // added to GW 2026-02-26
kDestroyUIPositionOverlay, // 0x10000179
kEnableUIPositionOverlay, // 0x1000017a, wparam = uint32_t enable
kMessage_0x1000017b,
kGuildHall, // 0x1000017C, wparam = gh key (uint32_t[4])
kMessage_0x1000017d,
kLeaveGuildHall, // 0x1000017E
kTravel, // 0x1000017F
kOpenWikiUrl, // 0x10000180, wparam = char* url
kMessage_0x10000181,
kMessage_0x10000182,
kSetPreGameContext_Value0, // wparam = uint32_t value
kMessage_0x10000184,
kGetPreGameContext_Value0, // lparam = *uint32_t value_out
kSetPreGameContext_Value1, // wparam = uint32_t value , added to GW 2026-02-06
kGetPreGameContext_Value1, // lparam = *uint32_t value_out, added to GW 2026-02-06
kMessage_0x10000186,
kMessage_0x10000187,
kMessage_0x10000188,
kMessage_0x10000189,
kMessage_0x1000018a,
kMessage_0x1000018b,
kMessage_0x1000018c,
kMessage_0x1000018d,
kAppendMessageToChat, // 0x1000018E, wparam = wchar_t* message
kMessage_0x1000018f,
kMessage_0x10000190,
kMessage_0x10000191,
kMessage_0x10000192,
kMessage_0x10000193,
kMessage_0x10000194,
kMessage_0x10000195,
kMessage_0x10000196,
kMessage_0x10000197,
kMessage_0x10000198,
kMessage_0x10000199,
kMessage_0x1000019a,
kMessage_0x1000019b,
kHideHeroPanel, // 0x1000019C, wparam = hero_id
kShowHeroPanel, // 0x1000019D, wparam = hero_id
kMessage_0x1000019e,
kMessage_0x1000019f,
kMessage_0x100001a0,
kGetInventoryAgentId, // 0x100001A1, wparam = 0, lparam = uint32_t* agent_id_out. Used to fetch which agent is selected
kEquipItem, // 0x100001A2, wparam = { item_id, agent_id }
kMoveItem, // 0x100001A3, wparam = { item_id, to_bag, to_slot, bool prompt }
kMessage_0x100001a4,
kMessage_0x100001a5,
kInitiateTrade, // 0x100001A6
kMessage_0x100001a7,
kMessage_0x100001a8,
kMessage_0x100001a9,
kMessage_0x100001aa,
kMessage_0x100001ab,
kMessage_0x100001ac,
kMessage_0x100001ad,
kMessage_0x100001ae,
kMessage_0x100001af,
kMessage_0x100001b0,
kMessage_0x100001b1,
kMessage_0x100001b2,
kMessage_0x100001b3,
kMessage_0x100001b4,
kMessage_0x100001b5,
kInventoryAgentChanged, // 0x100001B6, Triggered when inventory needs updating due to agent change; no args
kMessage_0x100001b7,
kMessage_0x100001b8,
kMessage_0x100001b9,
kMessage_0x100001ba,
kMessage_0x100001bb,
kMessage_0x100001bc,
kMessage_0x100001bd,
kPromptSaveTemplate, // 0x100001be
kOpenTemplate, // 0x100001Bf, wparam = GW::UI::ChatTemplate*
// GWCA Client to Server commands. Only added the ones that are used for hooks, everything else goes straight into GW
kSendLoadSkillTemplate = 0x30000000 | 0x3, // wparam = SkillbarMgr::SkillTemplate*
kSendPingWeaponSet = 0x30000000 | 0x4, // wparam = UIPacket::kSendPingWeaponSet*
kSendMoveItem = 0x30000000 | 0x5, // wparam = UIPacket::kSendMoveItem*
kSendMerchantRequestQuote = 0x30000000 | 0x6, // wparam = UIPacket::kSendMerchantRequestQuote*
kSendMerchantTransactItem = 0x30000000 | 0x7, // wparam = UIPacket::kSendMerchantTransactItem*
kSendUseItem = 0x30000000 | 0x8, // wparam = UIPacket::kSendUseItem*
kSendSetActiveQuest = 0x30000000 | 0x9, // wparam = uint32_t quest_id
kSendAbandonQuest = 0x30000000 | 0xA, // wparam = uint32_t quest_id
kSendChangeTarget = 0x30000000 | 0xB, // wparam = UIPacket::kSendChangeTarget* // e.g. tell the gw client to focus on a different target
kSendCallTarget = 0x30000000 | 0x13, // wparam = { uint32_t call_type, uint32_t agent_id } // also used to broadcast morale, death penalty, "I'm following X", etc
kSendDialog = 0x30000000 | 0x16, // wparam = dialog_id // internal use
kStartWhisper = 0x30000000 | 0x17, // wparam = UIPacket::kStartWhisper*
kGetSenderColor = 0x30000000 | 0x18, // wparam = UIPacket::kGetColor* // Get chat sender color depending on channel, output object passed by reference
kGetMessageColor = 0x30000000 | 0x19, // wparam = UIPacket::kGetColor* // Get chat message color depending on channel, output object passed by reference
kSendChatMessage = 0x30000000 | 0x1B, // wparam = UIPacket::kSendChatMessage*
kLogChatMessage = 0x30000000 | 0x1D, // wparam = UIPacket::kLogChatMessage*. Triggered when a message wants to be added to the persistent chat log.
kRecvWhisper = 0x30000000 | 0x1E, // wparam = UIPacket::kRecvWhisper*
kPrintChatMessage = 0x30000000 | 0x1F, // wparam = UIPacket::kPrintChatMessage*. Triggered when a message wants to be added to the in-game chat window.
kSendWorldAction = 0x30000000 | 0x20, // wparam = UIPacket::kSendWorldAction*
kSetRendererValue = 0x30000000 | 0x21, // wparam = UIPacket::kSetRendererValue
kIdentifyItem = 0x30000000 | 0x22, // wparam = UIPacket::kUseKitOnItem
kSalvageItem = 0x30000000 | 0x23 // wparam = UIPacket::kUseKitOnItem
};
static_assert(GW::UI::UIMessage::kOpenTemplate == (GW::UI::UIMessage)0x100001c4);
namespace UIPacket {
struct kDialogueMessage {
uint32_t agent_id;
wchar_t* sender;
wchar_t* message;
uint32_t duration;
uint32_t is_dialogue_1_or_2;
};
struct kErrorMessage {
uint32_t error_id;
wchar_t* message;
};
struct kAgentSkillPacket {
uint32_t agent_id;
GW::Constants::SkillID skill_id;
};
struct kLoadMapContext {
const wchar_t* file_name;
Constants::MapID map_id;
Constants::InstanceType map_type;
Vec2f spawn_point;
uint32_t h0014;
uint32_t h0018;
bool* success;
};
static_assert(sizeof(kLoadMapContext) == 0x20);
struct kMouseCoordsClick {
float offset_x;
float offset_y;
uint32_t h0008;
uint32_t h000c;
uint32_t* h0010;
uint32_t h0014;
};
struct kUseKitOnItem {
uint32_t item_id;
uint32_t kit_id;
};
struct kShowXunlaiChest {
uint32_t h0000 = 0;
bool storage_pane_unlocked = true;
bool anniversary_pane_unlocked = true;
};
struct kMoveItem {
uint32_t item_id;
uint32_t to_bag_index;
uint32_t to_slot;
uint32_t prompt;
};
struct kResize {
uint32_t h0000 = 0xffffffff;
float content_width;
float content_height;
float h000c = 0;
float h0010 = 0;
float content_width2;
float content_height2;
float margin_x;
float margin_y;
// ...
};
struct kTomeSkillSelection {
uint32_t item_id;
uint32_t h0004;
uint32_t h0008;
};
struct kMeasureContent {
float max_width; // Maximum width constraint
float max_height; // Maximum height constraint
float* size_output; // Pointer to output buffer for calculated size
uint32_t flags; // Layout flags (similar to the 0x100 flag we saw)
};
struct kSetLayout {
float field_0x0;
float field_0x4;
float field_0x8;
float field_0xc;
float available_width;
float available_height;
};
struct kSetAgentProfession {
AgentID agent_id;
uint32_t primary;
uint32_t secondary;
};
struct kWeaponSwap {
uint32_t weapon_bar_frame_id;
uint32_t weapon_set_id;
};
struct kWeaponSetChanged {
uint32_t h0000;
uint32_t h0004;
uint32_t h0008;
uint32_t h000c;
};
struct kChangeTarget {
uint32_t evaluated_target_id;
bool has_evaluated_target_changed;
uint32_t auto_target_id;
bool has_auto_target_changed;
uint32_t manual_target_id;
bool has_manual_target_changed;
};
struct kSendLoadSkillTemplate {
uint32_t agent_id;
SkillbarMgr::SkillTemplate* skill_template;
};
struct kVendorWindow {
Merchant::TransactionType transaction_type;
uint32_t unk;
uint32_t merchant_agent_id;
uint32_t is_pending;
};
struct kVendorQuote {
uint32_t item_id;
uint32_t price;
};
struct kVendorItems {
Merchant::TransactionType transaction_type;
uint32_t item_ids_count;
uint32_t* item_ids_buffer1; // world->merchant_items.buffer
uint32_t* item_ids_buffer2; // world->merchant_items2.buffer
};
struct kSetRendererValue {
uint32_t renderer_mode; // 0 for window, 2 for full screen
uint32_t metric_id; // TODO: Enum this!
uint32_t value;
};
struct kEffectAdd {
uint32_t agent_id;
Effect* effect;
};
struct kAgentSpeechBubble {
uint32_t agent_id;
wchar_t* message;
uint32_t h0008;
uint32_t h000c;
};
struct kAgentSkillStartedCast {
uint32_t agent_id;
Constants::SkillID skill_id;
float duration;
uint32_t h000c;
};
struct kPreStartSalvage {
uint32_t item_id;
uint32_t kit_id;
};
struct kServerActiveQuestChanged {
GW::Constants::QuestID quest_id;
GW::GamePos marker;
uint32_t h0024;
GW::Constants::MapID map_id;
uint32_t log_state;
};
struct kPrintChatMessage {
GW::Chat::Channel channel;
wchar_t* message;
FILETIME timestamp;
uint32_t is_reprint;
};
struct kPartyShowConfirmDialog {
uint32_t ui_message_to_send_to_party_frame;
uint32_t prompt_identitifier;
wchar_t* prompt_enc_str;
};
struct kUIPositionChanged {
uint32_t window_id;
UI::WindowPosition* position;
};
struct kPreferenceFlagChanged {
UI::FlagPreference preference_id;
uint32_t new_value;
};
struct kPreferenceValueChanged {
UI::NumberPreference preference_id;
uint32_t new_value;
};
struct kPreferenceEnumChanged {
UI::EnumPreference preference_id;
uint32_t enum_index;
};
struct kPartySearchInvite {
uint32_t source_party_search_id;
uint32_t dest_party_search_id;
};
struct kPostProcessingEffect {
uint32_t tint;
float amount;
};
struct kLogout {
uint32_t unknown;
uint32_t character_select;
};
static_assert(sizeof(kLogout) == 0x8);
struct kKeyAction {
uint32_t gw_key;
uint32_t modifiers = 0;
uint32_t state_flags = 0; // shift held = 0x4, ctrl = 0x2, alt = 0x1
};
struct kMouseClick {
uint32_t mouse_button; // 0x0 = left, 0x1 = middle, 0x2 = right
uint32_t is_doubleclick;
uint32_t unknown_type_screen_pos;
uint32_t h000c;
uint32_t h0010;
};
enum ActionState : uint32_t {
MouseDown = 0x6,
MouseUp = 0x7,
MouseClick = 0x8,
MouseDoubleClick = 0x9,
DragRelease = 0xb,
KeyDown = 0xe
};
struct kMouseAction {
uint32_t frame_id;
uint32_t child_offset_id;
ActionState current_state;
void* wparam = 0;
void* lparam = 0;
};
struct kWriteToChatLog {
GW::Chat::Channel channel;
wchar_t* message;
GW::Chat::Channel channel_dupe;
};
struct kPlayerChatMessage {
GW::Chat::Channel channel;
wchar_t* message;
uint32_t player_number;
};
struct kInteractAgent {
uint32_t agent_id;
bool call_target;
};
struct kSendChangeTarget {
uint32_t target_id;
uint32_t auto_target_id;
};
struct kSendCallTarget {
CallTargetType call_type;
uint32_t agent_id;
};
struct kGetColor {
Chat::Color* color;
GW::Chat::Channel channel;
};
struct kWriteToChatLogWithSender {
uint32_t channel;
wchar_t* message;
wchar_t* sender_enc;
};
struct kSendPingWeaponSet {
uint32_t agent_id;
uint32_t weapon_item_id;
uint32_t offhand_item_id;
};
struct kSendMoveItem {
uint32_t item_id;
uint32_t quantity;
uint32_t bag_index;
uint32_t slot;
};
struct kSendMerchantRequestQuote {
Merchant::TransactionType type;
uint32_t item_id;
};
struct kSendMerchantTransactItem {
Merchant::TransactionType type;
uint32_t h0004;
Merchant::QuoteInfo give;
uint32_t gold_recv;
Merchant::QuoteInfo recv;
};
struct kSendUseItem {
uint32_t item_id;
uint16_t quantity; // Unused, but would be cool
};
struct kSendChatMessage {
wchar_t* message;
uint32_t agent_id;
};
struct kLogChatMessage {
wchar_t* message;
GW::Chat::Channel channel;
};
struct kRecvWhisper {
uint32_t transaction_id;
wchar_t* from;
wchar_t* message;
};
struct kStartWhisper {
wchar_t* player_name;
};
struct kCompassDraw {
uint32_t player_number;
uint32_t session_id;
uint32_t number_of_points;
CompassPoint* points;
};
struct kObjectiveAdd {
uint32_t objective_id;
wchar_t* name;
uint32_t type;
};
struct kObjectiveComplete {
uint32_t objective_id;
};
struct kObjectiveUpdated {
uint32_t objective_id;
};
// Straight passthru of GW::Packets::StoC::ItemGeneral
struct kItemUpdated {
uint32_t item_id;
uint32_t model_file_id;
uint32_t type;
uint32_t unk1;
uint32_t extra_id; // Dye color
uint32_t materials;
uint32_t unk2;
uint32_t interaction; // Flags
uint32_t price;
uint32_t model_id;
uint32_t quantity;
wchar_t* enc_name;
uint32_t mod_struct_size;
uint32_t* mod_struct;
};
struct kInventorySlotUpdated {
uint32_t unk;
uint32_t item_id;
uint32_t bag_index;
uint32_t slot_id;
};
struct kSendWorldAction {
WorldActionId action_id;
GW::AgentID agent_id;
bool suppress_call_target; // 1 to block "I'm targetting X", but will also only trigger if the key thing is down
};
struct kAllyOrGuildMessage {
GW::Chat::Channel channel;
wchar_t* message;
wchar_t* sender;
wchar_t* guild_tag;
};
}
}
}
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct AccountContext;
GWCA_API AccountContext* GetAccountContext();
struct AccountUnlockedCount {
uint32_t id;
uint32_t unk1;
uint32_t unk2;
};
struct AccountUnlockedItemInfo {
uint32_t name_id;
uint32_t mod_struct_index; // Used to find mod struct in unlocked_pvp_items_mod_structs...
uint32_t mod_struct_size;
};
struct AccountContext {
/* +h0000 */ Array<AccountUnlockedCount> account_unlocked_counts; // e.g. number of unlocked storage panes
/* +h0010 */ uint8_t h0010[0xA4];
/* +h00b4 */ Array<uint32_t> unlocked_pvp_heros; // Unused, hero battles is no more :(
/* +h00c4 */ Array<uint32_t> h00c4;// If an item is unlocked, the mod struct is stored here. Use unlocked_pvp_items_info to find the index. Idk why, chaos reigns I guess
/* +h00e4 */ Array<AccountUnlockedItemInfo> unlocked_pvp_item_info; // If an item is unlocked, the details are stored here
/* +h00f4 */ Array<uint32_t> unlocked_pvp_items; // Bitwise array of which pvp items are unlocked
/* +h0104 */ uint8_t h0104[0x30]; // Some arrays, some linked lists, meh
/* +h0124 */ Array<uint32_t> unlocked_account_skills; // List of skills unlocked (but not learnt) for this account, i.e. skills that heros can use, tomes can unlock
/* +h0134 */ uint32_t account_flags;
};
static_assert(sizeof(AccountContext) == 0x138);
}
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct AgentContext;
GWCA_API AgentContext* GetAgentContext();
struct AgentMovement;
struct Agent;
struct AgentSummaryInfo {
struct AgentSummaryInfoSub {
/* +h0000 */ uint32_t h0000;
/* +h0004 */ uint32_t h0004;
/* +h0008 */ uint32_t gadget_id;
/* +h000C */ uint32_t h000C;
/* +h0010 */ wchar_t * gadget_name_enc;
/* +h0014 */ uint32_t h0014;
/* +h0018 */ uint32_t composite_agent_id; // 0x30000000 | player_id, 0x20000000 | npc_id etc
};
uint32_t h0000;
uint32_t h0004;
AgentSummaryInfoSub* extra_info_sub;
};
struct AgentContext {
/* +h0000 */ Array<void *> h0000;
/* +h0010 */ uint32_t h0010[5];
/* +h0024 */ uint32_t h0024; // function
/* +h0028 */ uint32_t h0028[2];
/* +h0030 */ uint32_t h0030; // function
/* +h0034 */ uint32_t h0034[2];
/* +h003C */ uint32_t h003C; // function
/* +h0040 */ uint32_t h0040[2];
/* +h0048 */ uint32_t h0048; // function
/* +h004C */ uint32_t h004C[2];
/* +h0054 */ uint32_t h0054; // function
/* +h0058 */ uint32_t h0058[11];
/* +h0084 */ Array<void *> h0084;
/* +h0094 */ uint32_t h0094; // this field and the next array are link together in a structure.
/* +h0098 */ Array<AgentSummaryInfo> agent_summary_info; // elements are of size 12. {ptr, func, ptr}
/* +h00A8 */ Array<void *> h00A8;
/* +h00B8 */ Array<void *> h00B8;
/* +h00C8 */ uint32_t rand1; // Number seems to be randomized quite a bit o.o seems to be accessed by textparser.cpp
/* +h00CC */ uint32_t rand2;
/* +h00D0 */ uint8_t h00D0[24];
/* +h00E8 */ Array<AgentMovement *> agent_movement;
/* +h00F8 */ Array<void *> h00F8;
/* +h0108 */ uint32_t h0108[0x11];
/* +h014C */ Array<void *> agent_array1;
/* +h015C */ Array<void *> agent_async_movement;
/* +h016C */ uint32_t h016C[0x10];
/* +h01AC */ uint32_t instance_timer;
//... more but meh
};
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
namespace Constants {
enum class Language;
enum class MapID : uint32_t;
enum class InstanceType;
}
struct CharContext;
GWCA_API CharContext* GetCharContext();
struct ObserverMatch;
struct ProgressBarContext {
int pips;
uint8_t color[4]; // RGBA
uint8_t background[4]; // RGBA
int unk[7];
float progress; // 0 ... 1
// possibly more
};
struct CharContext { // total: 0x42C
/* +h0000 */ Array<void *> h0000;
/* +h0010 */ uint32_t h0010;
/* +h0014 */ Array<void *> h0014;
/* +h0024 */ uint32_t h0024[4];
/* +h0034 */ Array<void *> h0034;
/* +h0044 */ Array<void *> h0044;
/* +h0054 */ uint32_t h0054[4]; // load head variables
/* +h0064 */ uint32_t player_uuid[4]; // uuid
/* +h0074 */ wchar_t player_name[0x14];
/* +h009C */ uint32_t h009C[20];
/* +h00EC */ Array<void *> h00EC;
/* +h00FC */ uint32_t h00FC[37]; // 40
/* +h0190 */ uint32_t world_flags;
/* +h0194 */ uint32_t token1; // world id
/* +h0198 */ GW::Constants::MapID map_id;
/* +h019C */ uint32_t is_explorable;
/* +h01A0 */ uint8_t host[0x18];
/* +h01B8 */ uint32_t token2; // player id
/* +h01BC */ uint32_t h01BC[25];
/* +h0220 */ uint32_t district_number;
/* +h0224 */ GW::Constants::Language language;
/* +h0228 */ GW::Constants::MapID observe_map_id;
/* +h022C */ GW::Constants::MapID current_map_id;
/* +h0230 */ Constants::InstanceType observe_map_type;
/* +h0234 */ Constants::InstanceType current_map_type;
/* +h0238 */ uint32_t h0238[5];
/* +h024C */ Array<ObserverMatch*> observer_matches;
/* +h025C */ uint32_t h025C[17];
/* +h02a0 */ uint32_t player_flags; // bitwise something
/* +h02A4 */ uint32_t player_number;
/* +h02A8 */ uint32_t h02A8[40];
/* +h0348 */ ProgressBarContext* progress_bar; // seems to never be nullptr
/* +h034C */ uint32_t h034C[27];
/* +h03B8 */ wchar_t player_email[0x40];
};
static_assert(sizeof(CharContext) == 0x438, "struct CharContext has incorrect size");
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
namespace GW {
struct Cinematic {
/* +h0000 */ uint32_t h0000;
/* +h0004 */ uint32_t h0004; // pointer to data
// ...
};
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
namespace GW {
struct GadgetInfo {
/* +h0000 */ uint32_t h0000;
/* +h0004 */ uint32_t h0004;
/* +h0008 */ uint32_t h0008;
/* +h000C */ wchar_t *name_enc;
};
struct GadgetContext {
/* +h0000 */ Array<GadgetInfo> GadgetInfo;
// ...
};
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <GWCA/Utilities/Export.h>
namespace GW {
struct GameContext;
GWCA_API GameContext* GetGameContext();
struct Cinematic;
struct MapContext;
struct TextParser;
struct CharContext;
struct ItemContext;
struct AgentContext;
struct GuildContext;
struct PartyContext;
struct TradeContext;
struct WorldContext;
struct GadgetContext;
struct AccountContext;
struct EventContext;
struct GameContext {
/* +h0000 */ void* h0000;
/* +h0004 */ void* h0004;
/* +h0008 */ AgentContext* agent; // Most functions that access are prefixed with Agent.
/* +h000C */ EventContext* event;
/* +h0010 */ void* h0010;
/* +h0014 */ MapContext* map; // Static object/collision data
/* +h0018 */ TextParser *text_parser;
/* +h001C */ void* h001C;
/* +h0020 */ uint32_t some_number; // 0x30 for me at the moment.
/* +h0024 */ void* h0024;
/* +h0028 */ AccountContext* account;
/* +h002C */ WorldContext* world; // Best name to fit it that I can think of.
/* +h0030 */ Cinematic *cinematic;
/* +h0034 */ void* h0034;
/* +h0038 */ GadgetContext* gadget;
/* +h003C */ GuildContext* guild;
/* +h0040 */ ItemContext* items;
/* +h0044 */ CharContext* character;
/* +h0048 */ void* h0048;
/* +h004C */ PartyContext* party;
/* +h0050 */ void* h0050;
/* +h0054 */ void* h0054;
/* +h0058 */ TradeContext* trade;
};
}
@@ -0,0 +1,16 @@
#pragma once
#include <GWCA/Utilities/Export.h>
namespace GW {
struct GameplayContext;
GWCA_API GameplayContext* GetGameplayContext();
// Static stuff used at runtime
struct GameplayContext {
/* +h0000 */ uint32_t h0000[0x13];
float mission_map_zoom;
uint32_t unk[10];
};
static_assert(sizeof(GameplayContext) == 0x78);
}
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include <GWCA/GameEntities/Guild.h>
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct GuildContext;
GWCA_API GuildContext* GetGuildContext();
typedef Array<Guild *> GuildArray;
typedef Array<GuildPlayer *> GuildRoster;
typedef Array<GuildHistoryEvent *> GuildHistory;
struct GuildContext { // total: 0x3BC/956
/* +h0000 */ uint32_t h0000;
/* +h0004 */ uint32_t h0004;
/* +h0008 */ uint32_t h0008;
/* +h000C */ uint32_t h000C;
/* +h0010 */ uint32_t h0010;
/* +h0014 */ uint32_t h0014;
/* +h0018 */ uint32_t h0018;
/* +h001C */ uint32_t h001C;
/* +h0020 */ Array<void *> h0020;
/* +h0030 */ uint32_t h0030;
/* +h0034 */ wchar_t player_name[20];
/* +h005C */ uint32_t h005C;
/* +h0060 */ uint32_t player_guild_index;
/* +h0064 */ GHKey player_gh_key;
/* +h0074 */ uint32_t h0074;
/* +h0078 */ wchar_t announcement[256];
/* +h0278 */ wchar_t announcement_author[20];
/* +h02A0 */ uint32_t player_guild_rank;
/* +h02A4 */ uint32_t h02A4;
/* +h02A8 */ Array<TownAlliance> factions_outpost_guilds;
/* +h02B8 */ uint32_t kurzick_town_count;
/* +h02BC */ uint32_t luxon_town_count;
/* +h02C0 */ uint32_t h02C0;
/* +h02C4 */ uint32_t h02C4;
/* +h02C8 */ uint32_t h02C8;
/* +h02CC */ GuildHistory player_guild_history;
/* +h02DC */ uint32_t h02DC[7];
/* +h02F8 */ GuildArray guilds;
/* +h0308 */ uint32_t h0308[4];
/* +h0318 */ Array<void *> h0318;
/* +h0328 */ uint32_t h0328;
/* +h032C */ Array<void *> h032C;
/* +h033C */ uint32_t h033C[7];
/* +h0358 */ GuildRoster player_roster;
//... end of what i care about
};
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct ItemContext;
GWCA_API ItemContext* GetItemContext();
struct Bag;
struct Item;
struct Inventory;
struct ItemContext { // total: 0x10C/268 BYTEs
/* +h0000 */ Array<void *> h0000;
/* +h0010 */ Array<void *> h0010;
/* +h0020 */ DWORD h0020;
/* +h0024 */ Array<Bag *> bags_array;
/* +h0034 */ char h0034[12];
/* +h0040 */ Array<void *> h0040;
/* +h0050 */ Array<void *> h0050;
/* +h0060 */ char h0060[88];
/* +h00B8 */ Array<Item *> item_array;
/* +h00C8 */ char h00C8[48];
/* +h00F8 */ Inventory *inventory;
/* +h00FC */ Array<void *> h00FC;
};
}
+200
View File
@@ -0,0 +1,200 @@
#pragma once
#include <GWCA/GameContainers/List.h>
#include <GWCA/GameContainers/Array.h>
#include <GWCA/GameContainers/ObjectPool.h>
#include <GWCA/GameContainers/PrioQ.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct PathingMap;
struct MapProp;
struct PropByType;
struct PropModelInfo;
namespace Constants {
enum class MapID : uint32_t;
}
typedef Array<PathingMap> PathingMapArray;
struct PropsContext {
/* +h0000 */ uint32_t pad1[0x1b];
/* +h006C */ Array<TList<PropByType>> propsByType;
/* +h007C */ uint32_t h007C[0xa];
/* +h00A4 */ Array<PropModelInfo> propModels;
/* +h00B4 */ uint32_t h00B4[0x38];
/* +h0194 */ Array<MapProp*> propArray;
};
static_assert(sizeof(PropsContext) == 0x1A4, "struct PropsContext has incorrect size");
struct MapStaticData {
/* +h0000 */ uint32_t h0000;
/* +h0004 */ uint32_t h0004;
/* +h0008 */ uint32_t h0008;
/* +h000C */ uint32_t h000C;
/* +h0010 */ uint32_t h0010;
/* +h0014 */ uint32_t trapezoidCount;
/* +h0018 */ PathingMapArray map;
/* +h0028 */ uint32_t h0028;
/* +h002C */ uint32_t h002C;
/* +h0030 */ uint32_t h0030;
/* +h0034 */ uint32_t h0034;
/* +h0038 */ uint32_t h0038;
/* +h003C */ uint32_t h003C;
/* +h0040 */ uint32_t h0040;
/* +h0044 */ uint32_t h0044;
/* +h0048 */ uint32_t h0048;
/* +h004C */ uint32_t h004C;
/* +h0050 */ uint32_t h0050;
/* +h0054 */ uint32_t h0054;
/* +h0058 */ uint32_t h0058;
/* +h005C */ uint32_t h005C;
/* +h0060 */ uint32_t h0060;
/* +h0064 */ uint32_t h0064;
/* +h0068 */ uint32_t h0068;
/* +h006C */ uint32_t h006C;
/* +h0070 */ uint32_t h0070;
/* +h0074 */ uint32_t h0074;
/* +h0078 */ uint32_t h0078;
/* +h007C */ uint32_t h007C;
/* +h0080 */ uint32_t h0080;
/* +h0084 */ uint32_t nextTrapezoidId; // Starts at 0, increment everytime a trapezoid is created. It's used to assign a unique trapezoid id to every trapezoid. Used for path finding.
/* +h0088 */ uint32_t h0088;
/* +h008C */ GW::Constants::MapID map_id;
/* +h0090 */ uint32_t h0090;
/* +h0094 */ uint32_t h0094;
/* +h0098 */ uint32_t h0098;
/* +h009C */ uint32_t h009C;
};
static_assert(sizeof(MapStaticData) == 0xA0, "struct MapStaticData has incorrect size");
// Those are planes that are blocked and can be unblocked at runtime. e.g., the gates in foundry
// Those aren't in the dat file, but sent from the server
typedef BaseArray<uint32_t> BlockedPlaneArray;
static_assert(sizeof(BlockedPlaneArray) == 0xC, "struct BlockedPlaneArray has incorrect size");
// The game leak this type name and it's `IPath::PathNode`
struct PathNode {
/* +h0000 */ uint32_t closed;
/* +h0004 */ float costToNode;
/* +h0008 */ PrioQLink<PathNode*> priority;
/* +h0018 */ GamePos nodePos; // This position is a guess on the best position to pass through. It's usually on one of the 4 edges of a trapezoid.
/* +h0024 */ struct PathingTrapezoid *currentTrapezoid;
/* +h0028 */ PathNode *parentPathMap;
};
static_assert(sizeof(PathNode) == 0x2C, "struct PathNode has incorrect size");
typedef BaseArray<PathNode*> PathNodeArray;
static_assert(sizeof(PathNodeArray) == 0xC, "struct PathNodeArray has incorrect size");
struct NodeCache {
/* +h0000 */ uint32_t* cachedCount;
/* +h0004 */ uint32_t m_mask;
/* +h0008 */ BaseArray<uint32_t> buffer;
};
static_assert(sizeof(NodeCache) == 0x14, "struct NodeCache has incorrect size");
struct PathWaypoint {
/* +h0000 */ float x;
/* +h0004 */ float y;
/* +h0008 */ float width;
/* +h000C */ float height;
/* +h0010 */ uint32_t plane;
/* +h0014 */ PathingTrapezoid *nextTrap;
};
static_assert(sizeof(PathWaypoint) == 0x18, "struct PathWaypoint has incorrect size");
struct PathContext {
/* +h0000 */ MapStaticData* staticData;
/* +h0004 */ BlockedPlaneArray blockedPlanes;
/* +h0010 */ PathNodeArray pathNodes; // This array of pathNodes are indexed with the trapezoid id.
/* +h001C */ NodeCache nodeCache;
/* +h0030 */ PrioQ<PathNode> openList;
/* +h0044 */ ObjectPool freeIPathNode;
/* +h0050 */ PathNodeArray allocatedPathNodes; // This is just an array of all allocated path nodes used to cleanup. The order is the allocation order.
/* +h005C */ uint32_t h005C;
/* +h0060 */ uint32_t h0060;
/* +h0064 */ Array<PathWaypoint> waypoints;
/* +h0074 */ Array<struct Node*> nodeStack;
/* +h0084 */ uint32_t h0084;
/* +h0088 */ uint32_t h0088;
/* +h008C */ uint32_t h008C;
/* +h0090 */ uint32_t h0090;
};
static_assert(sizeof(PathContext) == 0x94, "struct PathContext has incorrect size");
// The game can optionally load a DLL to do the path finding.
// The DLL is named "PathEngine.dll", but not clear if it's a 3rd party or just their name
// for development.
struct PathEngineContext {
/* +h0000 */ void **vtable;
/* +h0004 */ uint32_t h0004;
/* +h0008 */ uint32_t h0008;
/* +h000C */ void *user_data;
/* +h0010 */ HMODULE hDll;
/* +h0014 */ uint32_t pfnCreateInterface;
};
static_assert(sizeof(PathEngineContext) == 0x18, "struct PathEngineContext has incorrect size");
struct MapContext {
/* +h0000 */ uint32_t map_type; // less than 4
/* +h0004 */ Vec2f start_pos;
/* +h000c */ Vec2f end_pos;
/* +h0014 */ uint32_t h0014[6];
/* +h002C */ Array<void*> spawns1; // Seem to be arena spawns. struct is X,Y,unk 4 byte value,unk 4 byte value.
/* +h003C */ Array<void*> spawns2; // Same as above
/* +h004C */ Array<void*> spawns3; // Same as above
/* +h005C */ float h005C[6]; // Some trapezoid i think.
/* +h0074 */ PathContext* path;
/* +h0078 */ PathEngineContext* path_engine;
/* +h007C */ PropsContext* props;
/* +h0080 */ uint32_t h0080;
/* +h0084 */ void* terrain;
/* +h0088 */ uint32_t h0088;
/* +h008C */ GW::Constants::MapID map_id;
/* +h0090 */ uint32_t h0090;
/* +h0094 */ uint32_t h0094;
/* +h0098 */ uint32_t h0098;
/* +h009C */ uint32_t h009C;
/* +h00A0 */ uint32_t h00A0;
/* +h00A4 */ uint32_t h00A4;
/* +h00A8 */ uint32_t h00A8;
/* +h00AC */ uint32_t h00AC;
/* +h00B0 */ uint32_t h00B0;
/* +h00B4 */ uint32_t h00B4;
/* +h00B8 */ uint32_t h00B8;
/* +h00BC */ uint32_t h00BC;
/* +h00C0 */ uint32_t h00C0;
/* +h00C4 */ uint32_t h00C4;
/* +h00C8 */ uint32_t h00C8;
/* +h00CC */ uint32_t h00CC;
/* +h00D0 */ uint32_t h00D0;
/* +h00D4 */ uint32_t h00D4;
/* +h00D8 */ uint32_t h00D8;
/* +h00DC */ uint32_t h00DC;
/* +h00E0 */ uint32_t h00E0;
/* +h00E4 */ uint32_t h00E4;
/* +h00E8 */ uint32_t h00E8;
/* +h00EC */ uint32_t h00EC;
/* +h00F0 */ uint32_t h00F0;
/* +h00F4 */ uint32_t h00F4;
/* +h00F8 */ uint32_t h00F8;
/* +h00FC */ uint32_t h00FC;
/* +h0100 */ uint32_t h0100;
/* +h0104 */ uint32_t h0104;
/* +h0108 */ uint32_t h0108;
/* +h010C */ uint32_t h010C;
/* +h0110 */ uint32_t h0110;
/* +h0114 */ uint32_t h0114;
/* +h0118 */ uint32_t h0118;
/* +h011C */ uint32_t h011C;
/* +h0120 */ uint32_t h0120;
/* +h0124 */ uint32_t h0124;
/* +h0128 */ uint32_t h0128;
/* +h012C */ uint32_t h012C;
/* +h0130 */ void* zones;
/* +h0134 */ uint32_t h0134;
};
static_assert(sizeof(MapContext) == 0x138, "struct MapContext has incorrect size");
GWCA_API MapContext* GetMapContext();
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#include <GWCA/GameContainers/List.h>
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct PartyContext;
GWCA_API PartyContext* GetPartyContext();
struct PartyInfo;
struct PartySearch;
struct PartySearchContext {
uint32_t h0000;
uint32_t h0004;
uint32_t h0008;
uint32_t h000c;
uint32_t flags;
};
struct PartyContext { // total: 0x58/88
/* +h0000 */ uint32_t h0000;
/* +h0004 */ Array<void*> h0004;
/* +h0014 */ uint32_t flag;
/* +h0018 */ uint32_t h0018;
/* +h001C */ TList<PartyInfo> requests;
/* +h0028 */ uint32_t requests_count;
/* +h002C */ TList<PartyInfo> sending;
/* +h0038 */ uint32_t sending_count;
/* +h003C */ uint32_t h003C;
/* +h0040 */ Array<PartyInfo*> parties;
/* +h0050 */ uint32_t h0050;
/* +h0054 */ PartyInfo* player_party; // Players party
/* +h0058 */ uint32_t h0058[17];
/* +h009C */ uint32_t my_party_search_id;
/* +h00A0 */ uint32_t h00A0[8];
/* +h00C0 */ Array<PartySearch*> party_search;
bool InHardMode() const { return (flag & 0x10) > 0; }
bool IsDefeated() const { return (flag & 0x20) > 0; }
bool IsPartyLeader() const { return (flag >> 0x7) & 1; }
};
}
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct PreGameContext;
struct CharacterInformation;
GWCA_API PreGameContext* GetPreGameContext();
GWCA_API Array<CharacterInformation>* GetAvailableChars();
struct LoginCharacter {
uint32_t unk0; // Some kind of function call
wchar_t character_name[20];
};
// Character information available at login screen
struct CharacterInformation {
/* +h0000 */ uint32_t h0000[2];
/* +h0008 */ uint32_t uuid[4];
/* +h0018 */ wchar_t name[20];
/* +h0040 */ uint32_t props[17];
uint32_t GetMapId() const { return (props[0] >> 16) & 0xFFFF; }
uint32_t GetPrimaryProfession() const { return (props[2] >> 20) & 0xF; }
uint32_t GetSecondaryProfession() const { return (props[7] >> 10) & 0xF; }
uint32_t GetCampaign() const { return props[7] & 0xF; }
uint32_t GetLevel() const { return (props[7] >> 4) & 0x3F; }
bool IsPvP() const { return ((props[7] >> 9) & 0x1) == 0x1; }
};
struct PreGameContext {
/* +h0000 */ uint32_t frame_id;
/* +h0004 */ uint32_t h0004[72];
/* +h0124 */ uint32_t chosen_character_index;
/* +h0128 */ uint32_t h0128[6];
/* +h0140 */ uint32_t index_1;
/* +h0144 */ uint32_t index_2;
/* +h0148 */ GW::Array<LoginCharacter> chars;
};
}
// ============================================================
// C Interop API
// ============================================================
extern "C" {
GWCA_API void* GetAvailableChars();
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
namespace GW {
namespace Constants {
enum class Language;
}
struct TextCache {
/* +h0000 */ uint32_t h0000;
};
struct SubStruct1 {
/* +h0000 */ uint32_t h0000;
};
// Allocated @0078C243
struct SubStructUnk { // total: 0x54/84
/* +h0000 */ uint32_t AsyncDecodeStr_Callback;
/* +h0004 */ uint32_t AsyncDecodeStr_Param;
/* +h0008 */ uint32_t buffer_used; // if it's 1 then uses s1 & if it's 0 uses s2.
/* +h000C */ Array<wchar_t> s1;
/* +h001C */ Array<wchar_t> s2;
/* +h002C */ uint32_t h002C;
/* +h0030 */ uint32_t h0030; // tell us how many string will be enqueue before decoding.
/* +h0034 */ uint32_t h0034; // @0078B990
/* +h0038 */ uint8_t h0038[28];
};
struct TextParser {
/* +h0000 */ uint32_t h0000[8];
/* +h0020 */ wchar_t *dec_start;
/* +h0024 */ wchar_t *dec_end;
/* +h0028 */ uint32_t substitute_1; // related to h0020 & h0024
/* +h002C */ uint32_t substitute_2; // related to h0020 & h0024
/* +h0030 */ TextCache *cache;
/* +h0034 */ uint32_t h0034[75];
/* +h0160 */ uint32_t h0160; // @0078BEF5
/* +h0164 */ uint32_t h0164;
/* +h0168 */ uint32_t h0168; // set to 0 @0078BF34
/* +h016C */ uint32_t h016C[5];
/* +h0180 */ SubStruct1 *sub_struct;
/* +h0184 */ uint32_t h0184[19];
/* +h01d0 */ GW::Constants::Language language_id;
};
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <stdint.h>
#include <GWCA/GameContainers/Array.h>
namespace GW {
struct TradeItem {
uint32_t item_id;
uint32_t quantity;
};
struct TradePlayer {
uint32_t gold;
Array<TradeItem> items;
};
struct TradeContext {
enum TradeStatus {
TRADE_CLOSED = 0,
TRADE_INITIATED = 1,
TRADE_OFFER_SEND = 2,
TRADE_ACCEPTED = 4
};
/* +h0000 */ uint32_t flags; // this is actually a flags
/* +h0004 */ uint32_t h0004[3]; // Seemingly 3 null dwords
/* +h0010 */ TradePlayer player;
/* +h0024 */ TradePlayer partner;
// bool GetPartnerAccepted();
// bool GetPartnerOfferSent();
bool GetIsTradeOffered() const { return (flags & TRADE_OFFER_SEND) != 0; }
bool GetIsTradeInitiated() const { return (flags & TRADE_INITIATED) != 0; }
bool GetIsTradeAccepted() const { return (flags & TRADE_ACCEPTED) != 0; }
};
GWCA_API TradeContext* GetTradeContext();
}
+311
View File
@@ -0,0 +1,311 @@
#pragma once
#include <GWCA/GameContainers/Array.h>
#include <GWCA/GameContainers/GamePos.h>
#include <GWCA/Utilities/Export.h>
namespace GW {
struct WorldContext;
GWCA_API WorldContext* GetWorldContext();
namespace Constants {
enum class Profession : uint32_t;
}
typedef uint32_t ItemID;
enum class HeroBehavior : uint32_t;
struct NPC;
struct Quest;
struct Title;
struct Player;
struct HeroInfo;
struct HeroFlag;
struct MapAgent;
struct Skillbar;
struct AgentInfo;
struct AgentEffects;
struct PartyAttribute;
struct MissionMapIcon;
struct MissionObjective;
struct TitleTier;
typedef Array<NPC> NPCArray;
typedef Array<Quest> QuestLog;
typedef Array<Title> TitleArray;
typedef Array<ItemID> MerchItemArray;
typedef Array<Player> PlayerArray;
typedef Array<HeroFlag> HeroFlagArray;
typedef Array<HeroInfo> HeroInfoArray;
typedef Array<MapAgent> MapAgentArray;
typedef Array<Skillbar> SkillbarArray;
typedef Array<AgentInfo> AgentInfoArray;
typedef Array<AgentEffects> AgentEffectsArray;
typedef Array<MissionMapIcon> MissionMapIconArray;
typedef Array<PartyAttribute> PartyAttributeArray;
struct PartyAlly {
uint32_t agent_id;
uint32_t unk;
uint32_t composite_id;
};
static_assert(sizeof(PartyAlly) == 0xc);
namespace Constants {
enum class QuestID : uint32_t;
}
struct ControlledMinions {
uint32_t agent_id;
uint32_t minion_count;
};
struct DupeSkill {
uint32_t skill_id;
uint32_t count;
};
struct ProfessionState {
uint32_t agent_id;
GW::Constants::Profession primary;
GW::Constants::Profession secondary;
uint32_t unlocked_professions; // bitwise
uint32_t unk;
inline bool IsProfessionUnlocked(GW::Constants::Profession profession) const {
return (unlocked_professions & (1 << (uint32_t)profession)) != 0;
}
};
static_assert(sizeof(ProfessionState) == 0x14);
struct AccountInfo {
wchar_t* account_name;
uint32_t wins;
uint32_t losses;
uint32_t rating;
uint32_t qualifier_points;
uint32_t rank;
uint32_t tournament_reward_points;
};
static_assert(sizeof(AccountInfo) == 0x1c);
struct PartyMemberMoraleInfo {
uint32_t agent_id;
uint32_t agent_id_dup;
uint32_t unk[4];
uint32_t morale;
// ... unknown size
};
struct PartyMoraleLink {
uint32_t unk;
uint32_t unk2;
PartyMemberMoraleInfo* party_member_info;
};
static_assert(sizeof(PartyMoraleLink) == 0xc);
struct PetInfo {
uint32_t agent_id;
uint32_t owner_agent_id;
wchar_t* pet_name;
uint32_t model_file_id1;
uint32_t model_file_id2;
HeroBehavior behavior;
uint32_t locked_target_id;
};
static_assert(sizeof(PetInfo) == 0x1c);
struct PlayerControlledCharacter {
uint32_t field0_0x0;
uint32_t field1_0x4;
uint32_t field2_0x8;
uint32_t field3_0xc;
uint32_t field4_0x10;
uint32_t agent_id;
uint32_t composite_id; // 0x30000000 | player_number
uint32_t field7_0x1c;
uint32_t field8_0x20;
uint32_t field9_0x24;
uint32_t field10_0x28;
uint32_t field11_0x2c;
uint32_t field12_0x30;
uint32_t field13_0x34;
uint32_t field14_0x38;
uint32_t field15_0x3c;
uint32_t field16_0x40;
uint32_t field17_0x44;
uint32_t field18_0x48;
uint32_t field19_0x4c;
uint32_t field20_0x50;
uint32_t field21_0x54;
uint32_t field22_0x58;
uint32_t field23_0x5c;
uint32_t field24_0x60;
uint32_t more_flags;
uint32_t field26_0x68;
uint32_t field27_0x6c;
uint32_t field28_0x70;
uint32_t field29_0x74;
uint32_t field30_0x78;
uint32_t field31_0x7c;
uint32_t field32_0x80;
uint32_t field33_0x84;
uint32_t field34_0x88;
uint32_t field35_0x8c;
uint32_t field36_0x90;
uint32_t field37_0x94;
uint32_t field38_0x98;
uint32_t field39_0x9c;
uint32_t field40_0xa0;
uint32_t field41_0xa4;
uint32_t field42_0xa8;
uint32_t field43_0xac;
uint32_t field44_0xb0;
uint32_t field45_0xb4;
uint32_t field46_0xb8;
uint32_t field47_0xbc;
uint32_t field48_0xc0;
uint32_t field49_0xc4;
uint32_t field50_0xc8;
uint32_t field51_0xcc;
uint32_t field52_0xd0;
uint32_t field53_0xd4;
uint32_t field54_0xd8;
uint32_t field55_0xdc;
uint32_t field56_0xe0;
uint32_t field57_0xe4;
uint32_t field58_0xe8;
uint32_t field59_0xec;
uint32_t field60_0xf0;
uint32_t field61_0xf4;
uint32_t field62_0xf8;
uint32_t field63_0xfc;
uint32_t field64_0x100;
uint32_t field65_0x104;
uint32_t field66_0x108;
uint32_t flags;
uint32_t field68_0x110;
uint32_t field69_0x114;
uint32_t field70_0x118;
uint32_t field71_0x11c;
uint32_t field72_0x120;
uint32_t field73_0x124;
uint32_t field74_0x128;
uint32_t field75_0x12c;
uint32_t field76_0x130;
};
static_assert(sizeof(PlayerControlledCharacter) == 0x134);
struct WorldContext {
/* +h0000 */ AccountInfo* accountInfo;
/* +h0004 */ Array<wchar_t> message_buff;
/* +h0014 */ Array<wchar_t> dialog_buff;
/* +h0024 */ MerchItemArray merch_items;
/* +h0034 */ MerchItemArray merch_items2;
/* +h0044 */ uint32_t accumMapInitUnk0;
/* +h0048 */ uint32_t accumMapInitUnk1;
/* +h004C */ uint32_t accumMapInitOffset;
/* +h0050 */ uint32_t accumMapInitLength;
/* +h0054 */ uint32_t h0054;
/* +h0058 */ uint32_t accumMapInitUnk2;
/* +h005C */ uint32_t h005C[8];
/* +h007C */ MapAgentArray map_agents;
/* +h008C */ Array<PartyAlly> party_allies; // List of allies added to the current party
/* +h009C */ Vec3f all_flag;
/* +h00A8 */ uint32_t h00A8;
/* +h00AC */ PartyAttributeArray attributes;
/* +h00BC */ uint32_t h00BC[255];
/* +h04B8 */ Array<void *> h04B8;
/* +h04C8 */ Array<void *> h04C8;
/* +h04D8 */ uint32_t h04D8;
/* +h04DC */ Array<void *> h04DC;
/* +h04EC */ uint32_t h04EC[7];
/* +h0508 */ AgentEffectsArray party_effects;
/* +h0518 */ Array<void *> h0518;
/* +h0528 */ GW::Constants::QuestID active_quest_id;
/* +h052C */ QuestLog quest_log;
/* +h053C */ uint32_t h053C[10];
/* +h0564 */ Array<MissionObjective> mission_objectives;
/* +h0574 */ Array<uint32_t> henchmen_agent_ids;
/* +h0584 */ HeroFlagArray hero_flags;
/* +h0594 */ HeroInfoArray hero_info;
/* +h05A4 */ Array<void *> cartographed_areas; // Struct size = 0x20
/* +h05B4 */ uint32_t h05B4[2];
/* +h05BC */ Array<ControlledMinions> controlled_minion_count;
/* +h05CC */ Array<uint32_t> missions_completed;
/* +h05DC */ Array<uint32_t> missions_bonus;
/* +h05EC */ Array<uint32_t> missions_completed_hm;
/* +h05FC */ Array<uint32_t> missions_bonus_hm;
/* +h060C */ Array<uint32_t> unlocked_map;
/* +h061C */ uint32_t h061C[2];
/* +h0624 */ PartyMemberMoraleInfo* player_morale_info;
/* +h0628 */ uint32_t h028C;
/* +h062C */ Array<PartyMoraleLink> party_morale_related;
/* +h063C */ uint32_t h063C[16];
/* +h067C */ uint32_t player_number;
/* +h0680 */ PlayerControlledCharacter* playerControlledChar; // Struct size = 0x134 ?
/* +h0684 */ uint32_t is_hard_mode_unlocked;
/* +h0688 */ uint32_t h0688[2];
/* +h0690 */ uint32_t salvage_session_id;
/* +h0694 */ uint32_t h0694[5];
/* +h06A8 */ uint32_t playerTeamToken;
/* +h06AC */ Array<PetInfo> pets;
/* +h06BC */ Array<ProfessionState> party_profession_states; // Current state of primary/secondary/unlocked for current player and party heroes, used in skill window. aka attribStates
/* +h06CC */ Array<void *> h06CC;
/* +h06DC */ uint32_t h06DC;
/* +h06E0 */ Array<void *> h06E0;
/* +h06F0 */ SkillbarArray skillbar;
/* +h0700 */ Array<uint32_t> learnable_character_skills; // populated at skill trainer and when using signet of capture
/* +h0710 */ Array<uint32_t> unlocked_character_skills; // bit field
/* +h0720 */ Array<DupeSkill> duplicated_character_skills; // When res signet is bought more than once, its mapped into this array. Used in skill window.
/* +h0730 */ Array<void *> h0730;
/* +h0740 */ uint32_t experience;
/* +h0744 */ uint32_t experience_dupe;
/* +h0748 */ uint32_t current_kurzick;
/* +h074C */ uint32_t current_kurzick_dupe;
/* +h0750 */ uint32_t total_earned_kurzick;
/* +h0754 */ uint32_t total_earned_kurzick_dupe;
/* +h0758 */ uint32_t current_luxon;
/* +h075C */ uint32_t current_luxon_dupe;
/* +h0760 */ uint32_t total_earned_luxon;
/* +h0764 */ uint32_t total_earned_luxon_dupe;
/* +h0768 */ uint32_t current_imperial;
/* +h076C */ uint32_t current_imperial_dupe;
/* +h0770 */ uint32_t total_earned_imperial;
/* +h0774 */ uint32_t total_earned_imperial_dupe;
/* +h0778 */ uint32_t unk_faction4;
/* +h077C */ uint32_t unk_faction4_dupe;
/* +h0780 */ uint32_t unk_faction5;
/* +h0784 */ uint32_t unk_faction5_dupe;
/* +h0788 */ uint32_t level;
/* +h078C */ uint32_t level_dupe;
/* +h0790 */ uint32_t morale;
/* +h0794 */ uint32_t morale_dupe;
/* +h0798 */ uint32_t current_balth;
/* +h079C */ uint32_t current_balth_dupe;
/* +h07A0 */ uint32_t total_earned_balth;
/* +h07A4 */ uint32_t total_earned_balth_dupe;
/* +h07A8 */ uint32_t current_skill_points;
/* +h07AC */ uint32_t current_skill_points_dupe;
/* +h07B0 */ uint32_t total_earned_skill_points;
/* +h07B4 */ uint32_t total_earned_skill_points_dupe;
/* +h07B8 */ uint32_t max_kurzick;
/* +h07BC */ uint32_t max_luxon;
/* +h07C0 */ uint32_t max_balth;
/* +h07C4 */ uint32_t max_imperial;
/* +h07C8 */ uint32_t equipment_status;
/* +h07CC */ AgentInfoArray agent_infos;
/* +h07DC */ Array<void *> h07DC;
/* +h07EC */ MissionMapIconArray mission_map_icons;
/* +h07FC */ NPCArray npcs;
/* +h080C */ PlayerArray players;
/* +h081C */ TitleArray titles;
/* +h082C */ Array<TitleTier> title_tiers;
/* +h083C */ Array<uint32_t> vanquished_areas;
/* +h084C */ uint32_t foes_killed;
/* +h0850 */ uint32_t foes_to_kill;
//... couple more arrays after this
};
static_assert(sizeof(WorldContext) == 0x854); // Not the final size of WorldContext, but used to make sure offsets are correct.
}

Some files were not shown because too many files have changed in this diff Show More