namespace Aeshnidae.BuffBot; /// /// Taking pyreals out of a player's pack and giving change back. /// /// A deliberate copy of the arithmetic in Aeshnidae.Bank's BankService/Currencies rather /// than a call into it: the mod loader gives each mod its own collectible assembly context /// and sharing types across that boundary is the thing Mods\README.md warns will bite. The /// denomination table is retail's and does not change. /// /// Notes are taken SMALLEST first, so as few large notes are broken as possible, and /// anything over the asked amount is handed straight back as notes and coin. If the pack is /// too full to take the change (it cannot be, since what came out was larger, but a trade /// window or a full pack can still refuse), the change goes into the town's pot instead and /// the player is told - value is never destroyed. /// public static class Pyreals { /// Weenie to create. /// Face value in pyreals. /// Max stack size. public record Denomination(uint Wcid, long Unit, int MaxStack); /// Trade notes and pyreal coin, largest first - retail's values and stack limits. public static readonly Denomination[] Denominations = { new(20630, 250_000, 250), new(20629, 200_000, 250), new(20628, 150_000, 250), new( 2627, 100_000, 250), new( 7377, 75_000, 250), new( 2626, 50_000, 250), new( 7376, 25_000, 250), new( 7375, 20_000, 250), new( 7374, 15_000, 250), new( 2625, 10_000, 250), new( 2624, 5_000, 250), new( 2623, 1_000, 250), new( 2622, 500, 250), new( 2621, 100, 250), new( 273, 1, 25_000), }; public static long Carried(Player player) { long total = 0; foreach (var denom in Denominations) foreach (var stack in player.GetInventoryItemsOfWCID(denom.Wcid)) total += denom.Unit * (stack.StackSize ?? 1); return total; } /// /// Takes at least out of the pack, smallest denominations /// first, and says how much actually came out (never less than the amount unless the /// gather failed, in which case everything taken is handed straight back and 0 is /// returned). /// public static long Take(Player player, long amount) { if (amount <= 0) return 0; long collected = 0; foreach (var denom in Denominations.Reverse()) { if (collected >= amount) break; foreach (var stack in player.GetInventoryItemsOfWCID(denom.Wcid).ToList()) { if (collected >= amount) break; var size = stack.StackSize ?? 1; var stackValue = denom.Unit * size; var needed = amount - collected; if (stackValue <= needed) { if (player.TryConsumeFromInventoryWithNetworking(stack, size)) collected += stackValue; continue; } var take = (int)Math.Min(size, needed / denom.Unit); if (take > 0 && player.TryConsumeFromInventoryWithNetworking(stack, take)) collected += denom.Unit * take; // One more to cover any remainder; the excess comes back as change. if (collected < amount && take < size && player.TryConsumeFromInventoryWithNetworking(stack, 1)) collected += denom.Unit; } } if (collected < amount) { if (collected > 0) Give(player, collected); return 0; } return collected; } /// Hands pyreals back as notes and coin. Returns how much actually arrived. public static long Give(Player player, long amount) { long delivered = 0; foreach (var denom in Denominations) { while (amount - delivered >= denom.Unit) { var wanted = (amount - delivered) / denom.Unit; var count = denom.MaxStack > 1 ? (int)Math.Min(wanted, denom.MaxStack) : 1; var item = WorldObjectFactory.CreateNewWorldObject(denom.Wcid); if (item is null) break; if (denom.MaxStack > 1) item.SetStackSize(count); if (!player.TryCreateInInventoryWithNetworking(item)) { item.Destroy(); return delivered; } delivered += denom.Unit * count; } } return delivered; } }