package net.mcft.copy.backpacks; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos.MutableBlockPos; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerRespawnEvent; import net.mcft.copy.backpacks.WearableBackpacks; import net.mcft.copy.backpacks.api.BackpackHelper; import net.mcft.copy.backpacks.api.BackpackRegistry; import net.mcft.copy.backpacks.api.IBackpack; import net.mcft.copy.backpacks.api.IBackpackData; import net.mcft.copy.backpacks.api.IBackpackType; import net.mcft.copy.backpacks.block.entity.TileEntityBackpack; import net.mcft.copy.backpacks.container.SlotArmorBackpack; import net.mcft.copy.backpacks.item.DyeWashingHandler; import net.mcft.copy.backpacks.misc.BackpackCapability; import net.mcft.copy.backpacks.misc.util.WorldUtils; import net.mcft.copy.backpacks.network.MessageBackpackUpdate; public class ProxyCommon { public void preInit() { MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(WearableBackpacks.CONFIG); MinecraftForge.EVENT_BUS.register(new DyeWashingHandler()); CapabilityManager.INSTANCE.register(IBackpack.class, new BackpackCapability.Storage(), BackpackCapability.class); } public void init() { } // Attaching / sending capability @SubscribeEvent public void onAttachCapabilitiesEntity(AttachCapabilitiesEvent<Entity> event) { // Give entities that can wear backpacks the backpack capability. if (BackpackRegistry.canEntityWearBackpacks(event.getObject())) event.addCapability(BackpackCapability.IDENTIFIER, new BackpackCapability.Provider((EntityLivingBase)event.getObject())); } @SubscribeEvent public void onPlayerLogin(PlayerLoggedInEvent event) { sendBackpackStack(event.player, event.player); } @SubscribeEvent public void onPlayerChangedDimensionEvent(PlayerChangedDimensionEvent event) { sendBackpackStack(event.player, event.player); } @SubscribeEvent public void onPlayerRespawn(PlayerRespawnEvent event) { sendBackpackStack(event.player, event.player); } @SubscribeEvent public void onPlayerStartTracking(PlayerEvent.StartTracking event) { sendBackpackStack(event.getTarget(), event.getEntityPlayer()); } private void sendBackpackStack(Entity carrier, EntityPlayer player) { BackpackCapability backpack = (BackpackCapability)BackpackHelper.getBackpack(carrier); if (backpack != null) WearableBackpacks.CHANNEL.sendTo( MessageBackpackUpdate.stack(carrier, backpack.stack), player); } // Backpack interactions / events private boolean cancelOffHand = false; @SubscribeEvent public void onPlayerInteractBlock(PlayerInteractEvent.RightClickBlock event) { // This event is fired twice, once for each hand. Unfortunately there // is no way to set the result of the main hand interaction to SUCCESS // so the off hand one will be skipped. So: Hacky code! if (cancelOffHand) { cancelOffHand = false; if (event.getHand() == EnumHand.OFF_HAND) { event.setCanceled(true); return; } } // When players sneak-right-click the ground with an // empty hand, place down their equipped backpack. EntityPlayer player = event.getEntityPlayer(); World world = event.getWorld(); if (!player.isSneaking() || (event.getHand() != EnumHand.MAIN_HAND) || !player.getHeldItem(EnumHand.MAIN_HAND).isEmpty()) return; IBackpack backpack = BackpackHelper.getBackpack(player); if (backpack == null) return; // Try place the equipped backpack on the ground by using it. Also takes // care of setting the tile entity stack and data as well as unequipping. // See ItemBackpack.onItemUse. player.inventory.mainInventory.set(player.inventory.currentItem, backpack.getStack()); if (backpack.getStack().onItemUse( player, world, event.getPos(), EnumHand.MAIN_HAND, event.getFace(), 0.5F, 0.5F, 0.5F) == EnumActionResult.SUCCESS) { player.swingArm(EnumHand.MAIN_HAND); event.setCanceled(true); cancelOffHand = true; } else player.inventory.mainInventory.set(player.inventory.currentItem, ItemStack.EMPTY); } @SubscribeEvent public void onEntityInteract(PlayerInteractEvent.EntityInteract event) { // When players right-click equipped backpacks, interact with them. if (!WearableBackpacks.CONFIG.enableEquippedInteraction.get() || !(event.getTarget() instanceof EntityLivingBase)) return; EntityPlayer player = event.getEntityPlayer(); EntityLivingBase target = (EntityLivingBase)event.getTarget(); BackpackCapability backpack = (BackpackCapability)target.getCapability(IBackpack.CAPABILITY, null); if ((backpack == null) || !BackpackHelper.canInteractWithEquippedBackpack(player, target)) return; IBackpackType type = backpack.getType(); if (type == null) { WearableBackpacks.LOG.error("Backpack type was null when accessing equipped backpack"); return; } if (!player.world.isRemote && (backpack.getData() == null)) { IBackpackData data = type.createBackpackData(backpack.getStack()); if (data != null) { // Only show this error message if the backpack type is supposed to have backpack data. // Some backpacks might not need any to function, for example an ender backpack. WearableBackpacks.LOG.error("Backpack data was null when accessing equipped backpack"); backpack.setData(data); } } type.onEquippedInteract(player, target, backpack); } @SubscribeEvent public void onLivingUpdate(LivingUpdateEvent event) { // Update equipped backpacks and check // if they've been removed somehow. EntityLivingBase entity = event.getEntityLiving(); if (!BackpackRegistry.canEntityWearBackpacks(entity)) return; BackpackCapability backpack = (BackpackCapability)entity .getCapability(IBackpack.CAPABILITY, null); if (backpack == null) return; if (backpack.isChestArmor()) { if (entity instanceof EntityPlayer) SlotArmorBackpack.replace((EntityPlayer)entity); if (backpack.getStack().isEmpty()) { // Backpack has been removed somehow. backpack.getType().onFaultyRemoval(entity, backpack); backpack.setStack(ItemStack.EMPTY); } } if (!backpack.getStack().isEmpty()) { backpack.getType().onEquippedTick(entity, backpack); if (entity.world.isRemote) BackpackHelper.updateLidTicks(backpack, entity.posX, entity.posY + 1.0, entity.posZ); } } @SubscribeEvent public void onLivingDeath(LivingDeathEvent event) { // If an entity wearing a backpack dies, try // to place it as a block, or drop the items. EntityLivingBase entity = event.getEntityLiving(); World world = entity.world; if (world.isRemote) return; IBackpack backpack = BackpackHelper.getBackpack(entity); if (backpack == null) return; // If keep inventory is on, keep the backpack capability so we // can copy it over to the new player entity in onPlayerClone. EntityPlayer player = ((entity instanceof EntityPlayer) ? (EntityPlayer)entity : null); boolean keepInventory = world.getGameRules().getBoolean("keepInventory"); if ((player != null) && keepInventory) return; // Attempt to place the backpack as a block instead of dropping the items. if (WearableBackpacks.CONFIG.dropAsBlockOnDeath.get()) { List<BlockCoord> coords = new ArrayList<BlockCoord>(); for (int x = -2; x <= 2; x++) for (int z = -2; z <= 2; z++) coords.add(new BlockCoord(entity, x, z)); // Try to place the backpack on the ground nearby, // or look for a ground above or below to place it. Collections.sort(coords, new Comparator<BlockCoord>() { @Override public int compare(BlockCoord o1, BlockCoord o2) { if (o1.distance < o2.distance) return -1; else if (o1.distance > o2.distance) return 1; else return 0; } }); while (!coords.isEmpty()) { Iterator<BlockCoord> iter = coords.iterator(); while (iter.hasNext()) { BlockCoord coord = iter.next(); // Attempt to place and unequip the backpack at // this coordinate. If successful, we're done here. if (BackpackHelper.placeBackpack(world, coord, backpack.getStack(), entity, true)) { // TODO: I'm aware that this is not the cleanest solution. ((TileEntityBackpack)world.getTileEntity(coord)).setPlacedOnDeath(); return; } boolean replacable = world.getBlockState(coord).getBlock().isReplaceable(world, coord); coord.add(0, (replacable ? -1 : 1), 0); coord.moved += (replacable ? 1 : 5); if ((coord.getY() <= 0) || (coord.getY() > world.getHeight()) || (coord.moved > 24 - coord.distance * 4)) iter.remove(); } } } // In the case of regular backpacks, this causes their contents to be dropped. backpack.getType().onDeath(entity, backpack); // Drop the backpack as an item and remove it from the entity. if (!backpack.getStack().isEmpty()) WorldUtils.dropStackFromEntity(entity, backpack.getStack(), 4.0F); BackpackHelper.setEquippedBackpack(entity, ItemStack.EMPTY, null); } // Would use a method local class but "extractRangemapReplacedMain" gradle task doesn't like that. private static class BlockCoord extends MutableBlockPos { public double distance; public int moved = 0; public BlockCoord(Entity entity, int x, int z) { super((int)entity.posX + x, (int)entity.posY, (int)entity.posZ + z); distance = Math.sqrt(Math.pow(getX() + 0.5 - entity.posX, 2) + Math.pow(getY() + 0.5 - entity.posY, 2) + Math.pow(getZ() + 0.5 - entity.posZ, 2)); } } @SubscribeEvent public void onPlayerClone(PlayerEvent.Clone event) { // This comes into play when the "keepInventory" gamerule is on. // In that case, onLivingDeath will keep the backpack information, // so we can transfer it to the new player entity. IBackpack originalBackpack = BackpackHelper.getBackpack(event.getOriginal()); if (originalBackpack == null) return; EntityPlayer player = event.getEntityPlayer(); IBackpack clonedBackpack = player.getCapability(IBackpack.CAPABILITY, null); clonedBackpack.setStack(originalBackpack.getStack()); clonedBackpack.setData(originalBackpack.getData()); } }