namespace AcDream.Core.Items; /// /// Pure retail toolbar ammunition selection. /// Retail references: gmToolbarUI::UpdateAmmoID @ 0x004BF210 and /// gmToolbarUI::UpdateAmmoNumber @ 0x004BE9E0. /// public static class ToolbarAmmoPolicy { public readonly record struct Result(uint ObjectId, int DisplayCount) { public bool IsVisible => ObjectId != 0; } /// /// Resolve the first equipped missile weapon. If it is stackable, retail /// treats that stack as its own ammunition (thrown weapons). Otherwise it /// resolves the first separately equipped missile-ammo object. /// public static Result Resolve(IReadOnlyList inventoryPlacements) { ArgumentNullException.ThrowIfNull(inventoryPlacements); ClientObject? missileWeapon = FindAtLocation( inventoryPlacements, EquipMask.MissileWeapon); ClientObject? ammo = missileWeapon is { StackSizeMax: > 1 } ? missileWeapon : FindAtLocation(inventoryPlacements, EquipMask.MissileAmmo); return ammo is null ? default : new Result(ammo.ObjectId, ammo.StackSize == 0 ? 1 : ammo.StackSize); } private static ClientObject? FindAtLocation( IReadOnlyList inventoryPlacements, EquipMask location) { // ACCWeenieObject::GetObjectAtLocation @ 0x0058CE00 returns the first // inventory placement whose location mask intersects the requested mask. foreach (ClientObject item in inventoryPlacements) if ((item.CurrentlyEquippedLocation & location) != 0) return item; return null; } }