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;
}
}