Files
MinecraftForge/src/main/java/net/minecraftforge/items/ItemHandlerHelper.java
T
4ed9117799 Forge 1.19
* Bump pack.mcmeta formats
* 1.19 biome modifiers
* Mark ClientPlayerNetworkEvent.LoggedOutEvent's getters as nullable
* Add docs and package-info to client extension interfaces package
* Move RenderBlockOverlayEvent hooks to ForgeHooksClient
* Add package-infos to client events package
* Rename SoundLoadEvent to SoundEngineLoadEvent

This reduces confusion from consumers which may think the
name SoundLoadEvent refers to an individual sound being loaded rather
than the sound engine.
* Document and change SoundLoadEvent to fire on mod bus

Previously, it fired on both the mod bus and the Forge bus, which is
confusing for consumers.
* Delete SoundSetupEvent

Looking at its original implementation shows that there isn't an
appropriate place in the new sound code to reinsert the event, and the
place of 'sound engine/manager initialization event' is taken already by SoundLoadEvent.
* Perform some cleanup on client events

 - Removed nullable annotations from ClientPlayerNetworkEvent
 - Renamed #getPartialTicks methods to #getPartialTick, to be consistent
  with vanilla's naming of the partial tick
 - Cleanup documentation to remove line breaks, use the
  spelling 'cancelled' over
  'canceled', and improve docs on existing and
   new methods.
* Remove EntityEvent.CanUpdate

Closes MinecraftForge/MinecraftForge#6394
* Switch to Jetbrains nullability annotations
* New PlayLevelSoundEvent; replaces old PlaySoundAtEntityEvent
* Remove ForgeWorldPresetScreens
* Remove IForgeRegistryEntry
* Remove use of List<Throwable> in FML's CompletableFutures
* Add docs to mod loading stages, stages, and phases
* Gradle 7.4.2
* Use SLF4J in FMLLoader and other subprojects
* Switch dynamic versions in subprojects to pinned ones
* Switch ForgeRoot and MDK to FG plugin markers
* Configure Forge javadoc task

The task now uses a custom stylesheet with MCForge elements, and
configured to combine the generation from the four FML subprojects
(fmlloader, fmlcore, javafmllanguage, mclanguage) and the Forge project
into the javadoc output.
* Update docs/md files, for 1.19 update and the move away from IRC to Discord.
* Make "Potentially dangerous alternative prefix" a debug warning, not info.

Co-authored-by: Curle <curle@gemwire.uk>
Co-authored-by: sciwhiz12 <arnoldnunag12@gmail.com>
Signed-off-by: SizableShrimp <sizableshrimp@sizableshrimp.me>
2022-06-07 15:31:26 -05:00

219 lines
7.3 KiB
Java

/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.items;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraftforge.items.wrapper.PlayerMainInvWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ItemHandlerHelper
{
@NotNull
public static ItemStack insertItem(IItemHandler dest, @NotNull ItemStack stack, boolean simulate)
{
if (dest == null || stack.isEmpty())
return stack;
for (int i = 0; i < dest.getSlots(); i++)
{
stack = dest.insertItem(i, stack, simulate);
if (stack.isEmpty())
{
return ItemStack.EMPTY;
}
}
return stack;
}
public static boolean canItemStacksStack(@NotNull ItemStack a, @NotNull ItemStack b)
{
if (a.isEmpty() || !a.sameItem(b) || a.hasTag() != b.hasTag())
return false;
return (!a.hasTag() || a.getTag().equals(b.getTag())) && a.areCapsCompatible(b);
}
/**
* A relaxed version of canItemStacksStack that stacks itemstacks with different metadata if they don't have subtypes.
* This usually only applies when players pick up items.
*/
public static boolean canItemStacksStackRelaxed(@NotNull ItemStack a, @NotNull ItemStack b)
{
if (a.isEmpty() || b.isEmpty() || a.getItem() != b.getItem())
return false;
if (!a.isStackable())
return false;
// Metadata value only matters when the item has subtypes
// Vanilla stacks non-subtype items with different metadata together
// TODO Item subtypes, is this still necessary?
/* e.g. a stick with metadata 0 and a stick with metadata 1 stack
if (a.getHasSubtypes() && a.getMetadata() != b.getMetadata())
return false;
*/
if (a.hasTag() != b.hasTag())
return false;
return (!a.hasTag() || a.getTag().equals(b.getTag())) && a.areCapsCompatible(b);
}
@NotNull
public static ItemStack copyStackWithSize(@NotNull ItemStack itemStack, int size)
{
if (size == 0)
return ItemStack.EMPTY;
ItemStack copy = itemStack.copy();
copy.setCount(size);
return copy;
}
/**
* Inserts the ItemStack into the inventory, filling up already present stacks first.
* This is equivalent to the behaviour of a player picking up an item.
* Note: This function stacks items without subtypes with different metadata together.
*/
@NotNull
public static ItemStack insertItemStacked(IItemHandler inventory, @NotNull ItemStack stack, boolean simulate)
{
if (inventory == null || stack.isEmpty())
return stack;
// not stackable -> just insert into a new slot
if (!stack.isStackable())
{
return insertItem(inventory, stack, simulate);
}
int sizeInventory = inventory.getSlots();
// go through the inventory and try to fill up already existing items
for (int i = 0; i < sizeInventory; i++)
{
ItemStack slot = inventory.getStackInSlot(i);
if (canItemStacksStackRelaxed(slot, stack))
{
stack = inventory.insertItem(i, stack, simulate);
if (stack.isEmpty())
{
break;
}
}
}
// insert remainder into empty slots
if (!stack.isEmpty())
{
// find empty slot
for (int i = 0; i < sizeInventory; i++)
{
if (inventory.getStackInSlot(i).isEmpty())
{
stack = inventory.insertItem(i, stack, simulate);
if (stack.isEmpty())
{
break;
}
}
}
}
return stack;
}
/** giveItemToPlayer without preferred slot */
public static void giveItemToPlayer(Player player, @NotNull ItemStack stack) {
giveItemToPlayer(player, stack, -1);
}
/**
* Inserts the given itemstack into the players inventory.
* If the inventory can't hold it, the item will be dropped in the world at the players position.
*
* @param player The player to give the item to
* @param stack The itemstack to insert
*/
public static void giveItemToPlayer(Player player, @NotNull ItemStack stack, int preferredSlot)
{
if (stack.isEmpty()) return;
IItemHandler inventory = new PlayerMainInvWrapper(player.getInventory());
Level level = player.level;
// try adding it into the inventory
ItemStack remainder = stack;
// insert into preferred slot first
if (preferredSlot >= 0 && preferredSlot < inventory.getSlots())
{
remainder = inventory.insertItem(preferredSlot, stack, false);
}
// then into the inventory in general
if (!remainder.isEmpty())
{
remainder = insertItemStacked(inventory, remainder, false);
}
// play sound if something got picked up
if (remainder.isEmpty() || remainder.getCount() != stack.getCount())
{
level.playSound(null, player.getX(), player.getY() + 0.5, player.getZ(),
SoundEvents.ITEM_PICKUP, SoundSource.PLAYERS, 0.2F, ((level.random.nextFloat() - level.random.nextFloat()) * 0.7F + 1.0F) * 2.0F);
}
// drop remaining itemstack into the level
if (!remainder.isEmpty() && !level.isClientSide)
{
ItemEntity entityitem = new ItemEntity(level, player.getX(), player.getY() + 0.5, player.getZ(), remainder);
entityitem.setPickUpDelay(40);
entityitem.setDeltaMovement(entityitem.getDeltaMovement().multiply(0, 1, 0));
level.addFreshEntity(entityitem);
}
}
/**
* This method uses the standard vanilla algorithm to calculate a comparator output for how "full" the inventory is.
* This method is an adaptation of Container#calcRedstoneFromInventory(IInventory).
* @param inv The inventory handler to test.
* @return A redstone value in the range [0,15] representing how "full" this inventory is.
*/
public static int calcRedstoneFromInventory(@Nullable IItemHandler inv)
{
if (inv == null)
{
return 0;
}
else
{
int itemsFound = 0;
float proportion = 0.0F;
for (int j = 0; j < inv.getSlots(); ++j)
{
ItemStack itemstack = inv.getStackInSlot(j);
if (!itemstack.isEmpty())
{
proportion += (float)itemstack.getCount() / (float)Math.min(inv.getSlotLimit(j), itemstack.getMaxStackSize());
++itemsFound;
}
}
proportion = proportion / (float)inv.getSlots();
return Mth.floor(proportion * 14.0F) + (itemsFound > 0 ? 1 : 0);
}
}
}