Allow partial transactions due to missing inventory space (Fixes #88)

This commit is contained in:
Phoenix616 2018-01-05 18:30:06 +01:00
parent a0505e995e
commit b23a0d238e
4 changed files with 152 additions and 66 deletions

View File

@ -74,6 +74,23 @@ public class InventoryUtil {
return true;
}
/**
* Count amount of empty slots in an inventory
*
* @param inventory the inventory
* @return The amount of empty slots
*/
public static int countEmpty(Inventory inventory) {
int emptyAmount = 0;
for (ItemStack stack : getStorageContents(inventory)) {
if (MaterialUtil.isEmpty(stack)) {
emptyAmount++;
}
}
return emptyAmount;
}
/**
* Checks if the inventory has stock of this type
@ -92,6 +109,24 @@ public class InventoryUtil {
return true;
}
/**
* Checks if items fit in the inventory
*
* @param items Items to check
* @param inventory inventory
* @return Do the items fit inside the inventory?
*/
public static boolean fits(ItemStack[] items, Inventory inventory) {
ItemStack[] mergedItems = InventoryUtil.mergeSimilarStacks(items);
for (ItemStack item : mergedItems) {
if (!InventoryUtil.fits(item, inventory)) {
return false;
}
}
return true;
}
/**
* Checks if the item fits the inventory

View File

@ -70,7 +70,7 @@ public class Properties {
@ConfigurationComment("Do you want to allow other players to build a shop on a block where there's one already?")
public static boolean ALLOW_MULTIPLE_SHOPS_AT_ONE_BLOCK = false;
@ConfigurationComment("Can shops be used even when the seller doesn't have enough items? (The price will be scaled adequately to the item amount)")
@ConfigurationComment("Can shops be used even when the buyer/seller doesn't have enough items, space or money? (The price will be scaled adequately to the item amount)")
public static boolean ALLOW_PARTIAL_TRANSACTIONS = true;
@ConfigurationComment("Can '?' be put in place of item name in order for the sign to be auto-filled?")

View File

@ -16,6 +16,7 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@ -29,24 +30,22 @@ import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType.SELL
*/
public class PartialTransactionModule implements Listener {
@EventHandler(priority = EventPriority.LOW)
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public static void onPreBuyTransaction(PreTransactionEvent event) {
if (event.isCancelled() || event.getTransactionType() != BUY) {
if (event.getTransactionType() != BUY) {
return;
}
Player client = event.getClient();
ItemStack[] stock = event.getStock();
double price = event.getPrice();
double pricePerItem = event.getPrice() / InventoryUtil.countItems(stock);
double pricePerItem = event.getPrice() / InventoryUtil.countItems(event.getStock());
CurrencyAmountEvent currencyAmountEvent = new CurrencyAmountEvent(client);
ChestShop.callEvent(currencyAmountEvent);
BigDecimal walletMoney = currencyAmountEvent.getAmount();
CurrencyCheckEvent currencyCheckEvent = new CurrencyCheckEvent(BigDecimal.valueOf(price), client);
CurrencyCheckEvent currencyCheckEvent = new CurrencyCheckEvent(BigDecimal.valueOf(event.getPrice()), client);
ChestShop.callEvent(currencyCheckEvent);
if (!currencyCheckEvent.hasEnough()) {
@ -58,47 +57,54 @@ public class PartialTransactionModule implements Listener {
}
event.setPrice(amountAffordable * pricePerItem);
event.setStock(getCountedItemStack(stock, amountAffordable));
event.setStock(getCountedItemStack(event.getStock(), amountAffordable));
}
if (!InventoryUtil.hasItems(event.getStock(), event.getOwnerInventory())) {
ItemStack[] itemsHad = getItems(event.getStock(), event.getOwnerInventory());
int possessedItemCount = InventoryUtil.countItems(itemsHad);
if (possessedItemCount <= 0) {
event.setCancelled(NOT_ENOUGH_STOCK_IN_CHEST);
return;
}
event.setPrice(pricePerItem * possessedItemCount);
event.setStock(itemsHad);
}
if (!InventoryUtil.fits(event.getStock(), event.getClientInventory())) {
ItemStack[] itemsFit = getItemsThatFit(event.getStock(), event.getClientInventory());
int possessedItemCount = InventoryUtil.countItems(itemsFit);
if (possessedItemCount <= 0) {
event.setCancelled(NOT_ENOUGH_SPACE_IN_INVENTORY);
return;
}
event.setStock(itemsFit);
event.setPrice(pricePerItem * possessedItemCount);
}
UUID seller = event.getOwnerAccount().getUuid();
UUID seller = event.getOwner().getUniqueId();
CurrencyHoldEvent currencyHoldEvent = new CurrencyHoldEvent(BigDecimal.valueOf(price), seller, client.getWorld());
CurrencyHoldEvent currencyHoldEvent = new CurrencyHoldEvent(BigDecimal.valueOf(event.getPrice()), seller, client.getWorld());
ChestShop.callEvent(currencyHoldEvent);
if (!currencyHoldEvent.canHold()) {
event.setCancelled(SHOP_DEPOSIT_FAILED);
return;
}
stock = event.getStock();
if (!InventoryUtil.hasItems(stock, event.getOwnerInventory())) {
ItemStack[] itemsHad = getItems(stock, event.getOwnerInventory());
int posessedItemCount = InventoryUtil.countItems(itemsHad);
if (posessedItemCount <= 0) {
event.setCancelled(NOT_ENOUGH_STOCK_IN_CHEST);
return;
}
event.setPrice(pricePerItem * posessedItemCount);
event.setStock(itemsHad);
}
}
@EventHandler(priority = EventPriority.LOW)
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public static void onPreSellTransaction(PreTransactionEvent event) {
if (event.isCancelled() || event.getTransactionType() != SELL) {
if (event.getTransactionType() != SELL) {
return;
}
Player client = event.getClient();
UUID owner = event.getOwner().getUniqueId();
ItemStack[] stock = event.getStock();
UUID owner = event.getOwnerAccount().getUuid();
double price = event.getPrice();
double pricePerItem = event.getPrice() / InventoryUtil.countItems(stock);
double pricePerItem = event.getPrice() / InventoryUtil.countItems(event.getStock());
CurrencyAmountEvent currencyAmountEvent = new CurrencyAmountEvent(owner, client.getWorld());
ChestShop.callEvent(currencyAmountEvent);
@ -106,7 +112,7 @@ public class PartialTransactionModule implements Listener {
BigDecimal walletMoney = currencyAmountEvent.getAmount();
if (Economy.isOwnerEconomicallyActive(event.getOwnerInventory())) {
CurrencyCheckEvent currencyCheckEvent = new CurrencyCheckEvent(BigDecimal.valueOf(price), owner, client.getWorld());
CurrencyCheckEvent currencyCheckEvent = new CurrencyCheckEvent(BigDecimal.valueOf(event.getPrice()), owner, client.getWorld());
ChestShop.callEvent(currencyCheckEvent);
if (!currencyCheckEvent.hasEnough()) {
@ -118,31 +124,40 @@ public class PartialTransactionModule implements Listener {
}
event.setPrice(amountAffordable * pricePerItem);
event.setStock(getCountedItemStack(stock, amountAffordable));
event.setStock(getCountedItemStack(event.getStock(), amountAffordable));
}
}
if (!InventoryUtil.hasItems(event.getStock(), event.getClientInventory())) {
ItemStack[] itemsHad = getItems(event.getStock(), event.getClientInventory());
int possessedItemCount = InventoryUtil.countItems(itemsHad);
if (possessedItemCount <= 0) {
event.setCancelled(NOT_ENOUGH_STOCK_IN_INVENTORY);
return;
}
event.setPrice(pricePerItem * possessedItemCount);
event.setStock(itemsHad);
}
if (!InventoryUtil.fits(event.getStock(), event.getOwnerInventory())) {
ItemStack[] itemsFit = getItemsThatFit(event.getStock(), event.getOwnerInventory());
int possessedItemCount = InventoryUtil.countItems(itemsFit);
if (possessedItemCount <= 0) {
event.setCancelled(NOT_ENOUGH_STOCK_IN_CHEST);
return;
}
event.setStock(itemsFit);
event.setPrice(pricePerItem * possessedItemCount);
}
stock = event.getStock();
CurrencyHoldEvent currencyHoldEvent = new CurrencyHoldEvent(BigDecimal.valueOf(price), client);
CurrencyHoldEvent currencyHoldEvent = new CurrencyHoldEvent(BigDecimal.valueOf(event.getPrice()), client);
ChestShop.callEvent(currencyHoldEvent);
if (!currencyHoldEvent.canHold()) {
event.setCancelled(CLIENT_DEPOSIT_FAILED);
return;
}
if (!InventoryUtil.hasItems(stock, event.getClientInventory())) {
ItemStack[] itemsHad = getItems(stock, event.getClientInventory());
int posessedItemCount = InventoryUtil.countItems(itemsHad);
if (posessedItemCount <= 0) {
event.setCancelled(NOT_ENOUGH_STOCK_IN_INVENTORY);
return;
}
event.setPrice(pricePerItem * posessedItemCount);
event.setStock(itemsHad);
}
}
@ -207,4 +222,51 @@ public class PartialTransactionModule implements Listener {
return stacks.toArray(new ItemStack[stacks.size()]);
}
/**
* Make an array of items fit into an inventory.
*
* @param stock The items to fit in the inventory
* @param inventory The inventory to fit it in
* @return Whether or not the items fit into the inventory
*/
private static ItemStack[] getItemsThatFit(ItemStack[] stock, Inventory inventory) {
List<ItemStack> resultStock = new ArrayList<>();
int emptySlots = InventoryUtil.countEmpty(inventory);
ItemStack[] itemsInInventory = getItems(stock, inventory);
for (ItemStack item : stock) {
int maxStackSize = InventoryUtil.getMaxStackSize(item);
int free = 0;
for (ItemStack itemInInventory : itemsInInventory) {
if (MaterialUtil.equals(item, itemInInventory)) {
free = (maxStackSize - itemInInventory.getAmount()) % maxStackSize;
break;
}
}
if (free == 0 && emptySlots == 0) {
continue;
}
ItemStack clone = item.clone();
if (item.getAmount() > free) {
if (emptySlots > 0) {
int requiredSlots = (int) Math.ceil((item.getAmount() - free) / maxStackSize);
if (requiredSlots <= emptySlots) {
emptySlots = emptySlots - requiredSlots;
} else {
emptySlots = 0;
clone.setAmount(free + maxStackSize * emptySlots);
}
} else {
clone.setAmount(free);
}
}
resultStock.add(clone);
}
return (ItemStack[]) resultStock.toArray();
}
}

View File

@ -26,7 +26,7 @@ public class StockFittingChecker implements Listener {
Inventory shopInventory = event.getOwnerInventory();
ItemStack[] stock = event.getStock();
if (!itemsFitInInventory(stock, shopInventory)) {
if (!InventoryUtil.fits(stock, shopInventory)) {
event.setCancelled(NOT_ENOUGH_SPACE_IN_CHEST);
}
}
@ -40,19 +40,8 @@ public class StockFittingChecker implements Listener {
Inventory clientInventory = event.getClientInventory();
ItemStack[] stock = event.getStock();
if (!itemsFitInInventory(stock, clientInventory)) {
if (!InventoryUtil.fits(stock, clientInventory)) {
event.setCancelled(NOT_ENOUGH_SPACE_IN_INVENTORY);
}
}
private static boolean itemsFitInInventory(ItemStack[] items, Inventory inventory) {
ItemStack[] mergedItems = InventoryUtil.mergeSimilarStacks(items);
for (ItemStack item : mergedItems) {
if (!InventoryUtil.fits(item, inventory)) {
return false;
}
}
return true;
}
}