001package net.tnemc.item.paper;
002
003/*
004 * The New Item Library Minecraft Server Plugin
005 *
006 * Copyright (C) 2024 Daniel "creatorfromhell" Vidmar
007 *
008 * This program is free software; you can redistribute it and/or
009 * modify it under the terms of the GNU Lesser General Public
010 * License as published by the Free Software Foundation; either
011 * version 3 of the License, or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
016 * Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public License
019 * along with this program; if not, write to the Free Software Foundation,
020 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
021 */
022
023import net.tnemc.item.InventoryType;
024import net.tnemc.item.paper.platform.PaperItemPlatform;
025import net.tnemc.item.providers.CalculationsProvider;
026import net.tnemc.item.providers.ItemProvider;
027import net.tnemc.item.providers.VersionUtil;
028import org.bukkit.Bukkit;
029import org.bukkit.Material;
030import org.bukkit.OfflinePlayer;
031import org.bukkit.block.Container;
032import org.bukkit.entity.Item;
033import org.bukkit.entity.Player;
034import org.bukkit.inventory.Inventory;
035import org.bukkit.inventory.ItemStack;
036import org.bukkit.inventory.meta.BlockStateMeta;
037
038import java.util.ArrayList;
039import java.util.Collection;
040import java.util.Map;
041import java.util.Optional;
042import java.util.UUID;
043
044/**
045 * Represents a Bukkit implementation of the {@link CalculationsProvider}.
046 *
047 * @author creatorfromhell
048 * @since 0.1.5.0
049 */
050public class PaperCalculationsProvider implements CalculationsProvider<PaperItemStack, ItemStack, Inventory> {
051
052  /**
053   * Removes items from a collection based on certain criteria.
054   *
055   * @param left      The collection of items from which to remove items.
056   * @param player    The UUID of the player associated with the removal operation.
057   * @param setOwner  Indicates whether to set the owner of the removed items.(supports spigot/paper 1.16.5+)
058   *
059   * @return True if the removal operation was successful, false otherwise.
060   */
061  @Override
062  public boolean drop(final Collection<PaperItemStack> left, final UUID player, final boolean setOwner) {
063
064    final Player playerObj = Bukkit.getPlayer(player);
065    if(playerObj == null) {
066      return false;
067    }
068
069    for(final PaperItemStack stack : left) {
070
071      if(setOwner && VersionUtil.isOneSixteen(PaperItemPlatform.instance().version())) {
072
073        final Item it = playerObj.getWorld().dropItemNaturally(playerObj.getLocation(), stack.provider().locale(stack));
074        it.setOwner(player);
075        continue;
076      }
077
078      playerObj.getWorld().dropItemNaturally(playerObj.getLocation(), stack.provider().locale(stack));
079    }
080    return false;
081  }
082
083  /**
084   * Removes all items that are equal to the stack from an inventory.
085   *
086   * @param stack     The stack to compare to for removal from the inventory.
087   * @param inventory The inventory to remove the items from.
088   */
089  @Override
090  public int removeAll(final PaperItemStack stack, final Inventory inventory) {
091
092    final ItemStack compare = stack.provider().locale(stack).clone();
093    compare.setAmount(1);
094
095    int amount = 0;
096    final PaperItemStack comp = new PaperItemStack().of(compare);
097
098    for(int i = 0; i < inventory.getStorageContents().length; i++) {
099
100      final ItemStack item = inventory.getItem(i);
101      if(item == null) {
102        continue;
103      }
104
105      if(itemsEqual(comp, item)) {
106        amount += item.getAmount();
107        inventory.setItem(i, null);
108        continue;
109      }
110
111      if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final Container container) {
112
113        final Inventory containerInventory = container.getInventory();
114        for(int ci = 0; ci < containerInventory.getSize(); ci++) {
115
116          final ItemStack containerStack = inventory.getItem(ci);
117          if(containerStack == null) {
118
119            continue;
120          }
121
122          if(itemsEqual(comp, containerStack)) {
123
124            amount += item.getAmount();
125            inventory.setItem(ci, null);
126          }
127        }
128        container.update(true);
129        meta.setBlockState(container);
130        item.setItemMeta(meta);
131        inventory.setItem(i, item);
132      }
133    }
134    return amount;
135  }
136
137  /**
138   * Returns a count of items equal to the specific stack in an inventory.
139   *
140   * @param stack     The stack to get a count of.
141   * @param inventory The inventory to check.
142   *
143   * @return The total count of items in the inventory.
144   */
145  @Override
146  public int count(final PaperItemStack stack, final Inventory inventory) {
147
148    final ItemStack compare = stack.provider().locale(stack).clone();
149    compare.setAmount(1);
150
151    //TODO: make this more efficient
152    final PaperItemStack comp = new PaperItemStack().of(compare);
153    int amount = 0;
154
155    for(final ItemStack item : inventory.getStorageContents()) {
156
157      if(item == null) {
158        continue;
159      }
160
161      if(itemsEqual(comp, item)) {
162
163        amount += item.getAmount();
164      }
165
166      if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final Container container) {
167
168        final Inventory containerInventory = container.getInventory();
169        if(containerInventory.isEmpty()) {
170
171          continue;
172        }
173
174        for(int ci = 0; ci < containerInventory.getSize(); ci++) {
175
176          final ItemStack containerStack = containerInventory.getItem(ci);
177          if(containerStack == null) {
178
179            continue;
180          }
181
182          if(itemsEqual(comp, containerStack)) {
183
184            amount += containerStack.getAmount();
185          }
186        }
187      }
188    }
189    return amount;
190  }
191
192  /**
193   * Takes a collection of items from an inventory.
194   *
195   * @param items     The collection of items to remove.
196   * @param inventory The inventory to remove the items from.
197   */
198  @Override
199  public void takeItems(final Collection<PaperItemStack> items, final Inventory inventory) {
200
201    items.forEach(itemStack->removeItem(itemStack, inventory));
202  }
203
204  /**
205   * Adds a collection of net.tnemc.item stacks to an inventory, dropping them on the ground if it's
206   * a player inventory and overflow exists.
207   *
208   * @param items     The collection of items to add to the inventory.
209   * @param inventory The inventory to add the collection of items to.
210   */
211  @Override
212  public Collection<PaperItemStack> giveItems(final Collection<PaperItemStack> items, final Inventory inventory) {
213
214    final Collection<PaperItemStack> leftOver = new ArrayList<>();
215
216    for(final PaperItemStack item : items) {
217
218      if(item == null) {
219        continue;
220      }
221
222      final Map<Integer, ItemStack> left = inventory.addItem(item.provider().locale(item));
223      if(left.isEmpty()) {
224        continue;
225      }
226
227      for(final Map.Entry<Integer, ItemStack> entry : left.entrySet()) {
228        final ItemStack i = entry.getValue();
229
230        if(i == null || i.getType() == Material.AIR) {
231          continue;
232        }
233
234        leftOver.add(new PaperItemStack().of(i));
235      }
236    }
237    return leftOver;
238  }
239
240  /**
241   * Removes an ItemStack with a specific amount from an inventory.
242   *
243   * @param stack     The stack, with the correct amount, to remove.
244   * @param inventory The inventory to return the net.tnemc.item stack from.
245   *
246   * @return The remaining amount of items to remove.
247   */
248  @Override
249  public int removeItem(final PaperItemStack stack, final Inventory inventory) {
250
251    int left = stack.provider().locale(stack).clone().getAmount();
252
253    final ItemStack compare = stack.provider().locale(stack).clone();
254    compare.setAmount(1);
255
256    //TODO: improve this
257
258    final PaperItemStack comp = new PaperItemStack().of(compare);
259    final ItemProvider<ItemStack> provider = stack.provider();
260
261    for(int i = 0; i < inventory.getStorageContents().length; i++) {
262      if(left <= 0) break;
263
264      final ItemStack item = inventory.getItem(i);
265
266      if(item == null) continue;
267
268      if(provider.similar(stack, item)) {
269
270        if(item.getAmount() > left) {
271          item.setAmount(item.getAmount() - left);
272          inventory.setItem(i, item);
273          left = 0;
274          break;
275        }
276
277        if(item.getAmount() == left) {
278          inventory.setItem(i, null);
279          left = 0;
280          break;
281        }
282
283        left -= item.getAmount();
284        inventory.setItem(i, null);
285      }
286
287      if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final Container container) {
288
289        final Inventory containerInventory = container.getInventory();
290        for(int ci = 0; ci < containerInventory.getSize(); ci++) {
291
292          if(left <= 0) break;
293
294          final ItemStack containerStack = inventory.getItem(ci);
295          if(containerStack == null) {
296
297            continue;
298          }
299
300          if(!itemsEqual(comp, containerStack)) {
301            continue;
302          }
303
304          if(item.getAmount() > left) {
305
306            item.setAmount(item.getAmount() - left);
307            containerInventory.setItem(ci, item);
308            left = 0;
309            break;
310          }
311
312          if(item.getAmount() == left) {
313            containerInventory.setItem(ci, null);
314            left = 0;
315            break;
316          }
317
318          left -= item.getAmount();
319          containerInventory.setItem(ci, null);
320        }
321        container.update(true);
322        meta.setBlockState(container);
323        item.setItemMeta(meta);
324        inventory.setItem(i, item);
325      }
326    }
327    return left;
328  }
329
330  /**
331   * Used to locate an inventory for a UUID identifier.
332   *
333   * @param identifier The identifier to use for the search.
334   * @param type       The inventory type to return.
335   *
336   * @return An optional containing the inventory if it works, otherwise false.
337   */
338  @Override
339  public Optional<Inventory> inventory(final UUID identifier, final InventoryType type) {
340
341    final OfflinePlayer player = Bukkit.getOfflinePlayer(identifier);
342    if(player.isOnline() && player.getPlayer() != null) {
343
344      if(type.equals(InventoryType.ENDER_CHEST)) {
345        return Optional.of(player.getPlayer().getEnderChest());
346      } else {
347        return Optional.of(player.getPlayer().getInventory());
348      }
349    }
350    return Optional.empty();
351  }
352}