Skip to main content
The Client.Game namespace contains core gameplay systems that handle character actions, inventory management, status effects, and world state.

Key Classes

ActionManager

Manages action execution, cooldowns, casting, and queuing. Struct Layout:
// Client::Game::ActionManager
[StructLayout(LayoutKind.Explicit, Size = 0x800)]
public unsafe partial struct ActionManager {
    [FieldOffset(0x08)] public float AnimationLock;
    [FieldOffset(0x24)] public uint CastSpellId;
    [FieldOffset(0x28)] public ActionType CastActionType;
    [FieldOffset(0x2C)] public uint CastActionId;
    [FieldOffset(0x30)] public float CastTimeElapsed;
    [FieldOffset(0x34)] public float CastTimeTotal;
    [FieldOffset(0x38)] public GameObjectId CastTargetId;
    [FieldOffset(0x60)] public ComboDetail Combo;
    [FieldOffset(0x68)] public bool ActionQueued;
    [FieldOffset(0x184)] internal FixedSizeArray80<RecastDetail> _cooldowns;
}
Singleton Access:
var actionManager = ActionManager.Instance();
Key Methods:
  • UseAction(ActionType, uint, ulong, uint, UseActionMode, uint, bool*) - Execute an action
  • GetActionStatus(ActionType, uint, ulong, bool, bool, uint*) - Check if action can be used
  • GetRecastTime(ActionType, uint) - Get cooldown remaining
  • IsActionOffCooldown(ActionType, uint) - Check if action is available
  • GetRecastGroupDetail(int) - Get cooldown details for a recast group
Usage Example:
var am = ActionManager.Instance();
if (am->IsActionOffCooldown(ActionType.Action, 7535)) {
    am->UseAction(ActionType.Action, 7535, targetId);
}

// Check cast progress
if (am->CastSpellId != 0) {
    var progress = am->CastTimeElapsed / am->CastTimeTotal;
    Console.WriteLine($"Casting: {progress * 100:F1}%");
}
RecastDetail Structure:
[StructLayout(LayoutKind.Explicit, Size = 0x14)]
public struct RecastDetail {
    [FieldOffset(0x0)] public bool IsActive;
    [FieldOffset(0x4)] public uint ActionId;
    [FieldOffset(0x8)] public float Elapsed;
    [FieldOffset(0xC)] public float Total;
}

InventoryManager

Manages all player inventory, equipment, and currency. Struct Layout:
// Client::Game::InventoryManager
[StructLayout(LayoutKind.Explicit, Size = 0x3730)]
public unsafe partial struct InventoryManager {
    [FieldOffset(0x1E00)] public uint NextContextId;
    [FieldOffset(0x1E08)] public InventoryContainer* Inventories;
    [FieldOffset(0x217C)] public TradeState TradeLocalState;
    [FieldOffset(0x2180)] public TradeState TradeRemoteState;
}
Singleton Access:
var inventoryManager = InventoryManager.Instance();
Key Methods:
  • GetInventoryContainer(InventoryType) - Get a specific inventory container
  • GetInventorySlot(InventoryType, int) - Get item in a slot
  • GetInventoryItemCount(uint, bool, bool, bool, short) - Count items by ID
  • MoveItemSlot(InventoryType, ushort, InventoryType, ushort, bool) - Move items
  • GetGil() - Get current gil amount
  • GetEmptySlotsInBag() - Get free inventory space
Usage Example:
var im = InventoryManager.Instance();
var gil = im->GetGil();
var freeSlots = im->GetEmptySlotsInBag();

// Get item from inventory
var item = im->GetInventorySlot(InventoryType.Inventory1, 0);
if (item != null && item->ItemId != 0) {
    Console.WriteLine($"Slot 0: Item {item->ItemId} x{item->Quantity}");
}

// Count specific items
var potionCount = im->GetInventoryItemCount(4554, isHq: false);

StatusManager

Tracks buffs and debuffs on a character. Struct Layout:
// Client::Game::StatusManager
[StructLayout(LayoutKind.Explicit, Size = 0x3E0)]
public unsafe partial struct StatusManager {
    [FieldOffset(0x0)] public Character.Character* Owner;
    [FieldOffset(0x8)] internal FixedSizeArray60<Status> _status;
    [FieldOffset(0x3D8)] public byte NumValidStatuses;
}

[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public struct Status {
    [FieldOffset(0x0)] public ushort StatusId;
    [FieldOffset(0x2)] public ushort Param;  // Stack count or item ID
    [FieldOffset(0x4)] public float RemainingTime;
    [FieldOffset(0x8)] public GameObjectId SourceObject;
}
Key Methods:
  • HasStatus(uint, uint) - Check if status is active
  • GetStatusIndex(uint, uint) - Get index of status
  • GetRemainingTime(int) - Get time remaining on status
  • ExecuteStatusOff(uint, uint) - Remove a buff (static)
Usage Example:
var character = /* get character */;
var statusMgr = &character->StatusManager;

if (statusMgr->HasStatus(1209)) { // Sprint
    var index = statusMgr->GetStatusIndex(1209);
    var remaining = statusMgr->GetRemainingTime(index);
    Console.WriteLine($"Sprint: {remaining:F1}s remaining");
}

// Iterate all statuses
for (int i = 0; i < statusMgr->NumValidStatuses; i++) {
    var status = statusMgr->Status[i];
    Console.WriteLine($"Status {status.StatusId}: {status.RemainingTime:F1}s");
}

Character

Represents a character in the world (player, NPC, enemy). Struct Layout:
// Client::Game::Character::Character
//   Client::Game::Object::GameObject
[Inherits<GameObject>, Inherits<CharacterData>]
[StructLayout(LayoutKind.Explicit, Size = 0x2370)]
public unsafe partial struct Character {
    [FieldOffset(0x600)] public MovementStateOptions MovementState;
    [FieldOffset(0x630)] public EmoteController EmoteController;
    [FieldOffset(0x670)] public MountContainer Mount;
    [FieldOffset(0x6F8)] public DrawDataContainer DrawData;
    [FieldOffset(0x1988)] public VfxContainer Vfx;
    [FieldOffset(0x2308)] public GameObjectId TargetId;
    [FieldOffset(0x2310)] public GameObjectId SoftTargetId;
    [FieldOffset(0x2358)] public ulong ContentId;
    [FieldOffset(0x2360)] public ushort CurrentWorld;
    [FieldOffset(0x2362)] public ushort HomeWorld;
}
Inherited from GameObject:
[FieldOffset(0x30)] internal FixedSizeArray64<byte> _name;
[FieldOffset(0x78)] public uint EntityId;
[FieldOffset(0x8C)] public ushort ObjectIndex;
[FieldOffset(0x90)] public ObjectKind ObjectKind;
[FieldOffset(0xB0)] public Vector3 Position;
[FieldOffset(0xC0)] public float Rotation;
[FieldOffset(0xD0)] public float HitboxRadius;
Key Methods:
  • GetTargetId() - Get hard target (handles LocalPlayer)
  • GetSoftTargetId() - Get soft target
  • HasStatus(uint) - Check for status effect
  • IsMounted() - Check if on mount
  • IsJumping() - Check if jumping/falling
Usage Example:
var character = /* get character from object table */;
var name = character->GameObject.Name;
var pos = character->GameObject.Position;
var targetId = character->GetTargetId();

if (character->IsMounted()) {
    var mountId = character->Mount.MountId;
    Console.WriteLine($"{name} is on mount {mountId}");
}

Other Important Classes

GameMain

Central game state singleton.
var gameMain = GameMain.Instance();
var currentTerritory = gameMain->CurrentTerritoryTypeId;
var isPvP = GameMain.IsInPvPArea();

GameObject

Base class for all world objects. Access via ObjectTable:
var objectTable = ObjectTable.Instance();
for (int i = 0; i < objectTable->Length; i++) {
    var obj = objectTable->GetObjectByIndex(i);
    if (obj != null && obj->ObjectKind == ObjectKind.Player) {
        Console.WriteLine($"Player: {obj->Name} at {obj->Position}");
    }
}

Common Patterns

Checking Action Availability

var am = ActionManager.Instance();
var actionId = 7535u; // Example action

// Check all conditions
var status = am->GetActionStatus(ActionType.Action, actionId, targetId);
if (status == 0) {
    // Action can be used
    am->UseAction(ActionType.Action, actionId, targetId);
}

Iterating Inventory

var im = InventoryManager.Instance();
var container = im->GetInventoryContainer(InventoryType.Inventory1);

for (int i = 0; i < container->Size; i++) {
    var item = container->GetInventorySlot(i);
    if (item != null && item->ItemId != 0) {
        Console.WriteLine($"Slot {i}: {item->ItemId} x{item->Quantity}");
    }
}

See Also

Build docs developers (and LLMs) love