package openblocks.common.item; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import java.util.List; import net.minecraft.block.Block; import net.minecraft.client.model.ModelBiped; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer.EnumStatus; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.Item; import net.minecraft.item.ItemArmor; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.play.server.S0APacketUseBed; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.IChatComponent; import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.Constants; import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; import openblocks.OpenBlocks; import openblocks.api.SleepingBagUseEvent; import openblocks.asm.EntityPlayerVisitor; import openblocks.client.model.ModelSleepingBag; import openmods.infobook.BookDocumentation; import openmods.reflection.FieldAccess; import openmods.utils.BlockUtils; import openmods.utils.ItemUtils; import openmods.utils.TagUtils; @BookDocumentation(customName = "sleepingbag") public class ItemSleepingBag extends ItemArmor { private static final String TAG_SPAWN = "Spawn"; private static final String TAG_POSITION = "Position"; private static final String TAG_SLEEPING = "Sleeping"; private static final String TAG_SLOT = "Slot"; private static final int ARMOR_CHESTPIECE_TYPE = 1; private static final int ARMOR_CHESTPIECE_SLOT = 2; private static final FieldAccess<Boolean> IS_SLEEPING = FieldAccess.create(EntityPlayer.class, "sleeping", "field_71083_bS"); private static final FieldAccess<Integer> SLEEPING_TIMER = FieldAccess.create(EntityPlayer.class, "sleepTimer", "field_71076_b"); public static final String TEXTURE_SLEEPINGBAG = "openblocks:textures/models/sleepingbag.png"; public ItemSleepingBag() { super(ArmorMaterial.IRON, 2, ARMOR_CHESTPIECE_TYPE); setCreativeTab(OpenBlocks.tabOpenBlocks); } @Override public String getArmorTexture(ItemStack stack, Entity entity, int slot, String type) { return TEXTURE_SLEEPINGBAG; } @Override @SideOnly(Side.CLIENT) public ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, int armorSlot) { return armorSlot == ARMOR_CHESTPIECE_TYPE? ModelSleepingBag.instance : null; } @Override public ItemStack onItemRightClick(ItemStack sleepingBagStack, World world, EntityPlayer player) { if (!world.isRemote) { ItemStack currentArmor = getChestpieceSlot(player); if (currentArmor != null) currentArmor = currentArmor.copy(); final ItemStack sleepingBagCopy = sleepingBagStack.copy(); NBTTagCompound tag = ItemUtils.getItemTag(sleepingBagCopy); tag.setInteger(TAG_SLOT, player.inventory.currentItem); setChestpieceSlot(player, sleepingBagCopy); if (currentArmor != null) return currentArmor; sleepingBagStack.stackSize = 0; } return sleepingBagStack; } @Override public boolean isValidArmor(ItemStack stack, int armorType, Entity entity) { return armorType == ARMOR_CHESTPIECE_TYPE; } @Override public void onArmorTick(World world, EntityPlayer player, ItemStack itemStack) { if (!(player instanceof EntityPlayerMP)) return; if (player.isPlayerSleeping()) return; NBTTagCompound tag = ItemUtils.getItemTag(itemStack); if (!EntityPlayerVisitor.IsInBedHookSuccess) { player.addChatComponentMessage(new ChatComponentTranslation("openblocks.misc.sleeping_bag_broken")); getOutOfSleepingBag(player); } else if (tag.getBoolean(TAG_SLEEPING)) { // player just woke up restoreOriginalSpawn(player, tag); restoreOriginalPosition(player, tag); tag.removeTag(TAG_SLEEPING); getOutOfSleepingBag(player); } else { // player just put in on final int posX = MathHelper.floor_double(player.posX); final int posY = MathHelper.floor_double(player.posY); final int posZ = MathHelper.floor_double(player.posZ); if (canPlayerSleep(player, world, posX, posY, posZ)) { storeOriginalSpawn(player, tag); storeOriginalPosition(player, tag); tag.setBoolean(TAG_SLEEPING, true); sleepSafe((EntityPlayerMP)player, world, posX, posY, posZ); } else getOutOfSleepingBag(player); } } private static void sleepSafe(EntityPlayerMP player, World world, int x, int y, int z) { if (player.isRiding()) player.mountEntity(null); IS_SLEEPING.set(player, true); SLEEPING_TIMER.set(player, 0); player.playerLocation = new ChunkCoordinates(x, y, z); player.motionX = player.motionZ = player.motionY = 0.0D; world.updateAllPlayersSleepingFlag(); S0APacketUseBed sleepPacket = new S0APacketUseBed(player, x, y, z); player.getServerForPlayer().getEntityTracker().func_151247_a(player, sleepPacket); player.playerNetServerHandler.sendPacket(sleepPacket); } private static EnumStatus vanillaCanSleep(EntityPlayer player, World world, int x, int y, int z) { PlayerSleepInBedEvent event = new PlayerSleepInBedEvent(player, x, y, z); MinecraftForge.EVENT_BUS.post(event); if (event.result != null) return event.result; if (!world.provider.isSurfaceWorld()) return EntityPlayer.EnumStatus.NOT_POSSIBLE_HERE; if (world.isDaytime()) return EntityPlayer.EnumStatus.NOT_POSSIBLE_NOW; List<?> list = world.getEntitiesWithinAABB(EntityMob.class, AxisAlignedBB.getBoundingBox(x - 8, y - 5, z - 8, x + 8, y + 5, z + 8)); if (!list.isEmpty()) return EntityPlayer.EnumStatus.NOT_SAFE; return EntityPlayer.EnumStatus.OK; } private static boolean canPlayerSleep(EntityPlayer player, World world, int x, int y, int z) { if (player.isPlayerSleeping() || !player.isEntityAlive()) return false; if (!isNotSuffocating(world, x, y, z) || !isSolidEnough(world, x, y - 1, z)) { player.addChatComponentMessage(new ChatComponentTranslation("openblocks.misc.oh_no_ground")); return false; } final EnumStatus status = vanillaCanSleep(player, world, x, y, z); final SleepingBagUseEvent evt = new SleepingBagUseEvent(player, status); evt.playerChat = findDefaultChatComponent(status); MinecraftForge.EVENT_BUS.post(evt); switch (evt.getResult()) { case ALLOW: return true; case DEFAULT: if (evt.playerChat != null) player.addChatComponentMessage(evt.playerChat); return evt.defaultCanSleep(); case DENY: default: if (evt.playerChat != null) player.addChatComponentMessage(evt.playerChat); return false; } } private static IChatComponent findDefaultChatComponent(EnumStatus status) { switch (status) { case NOT_POSSIBLE_NOW: return new ChatComponentTranslation("tile.bed.noSleep"); case NOT_SAFE: return new ChatComponentTranslation("tile.bed.notSafe"); default: return null; } } private static boolean isNotSuffocating(World world, int x, int y, int z) { return world.getBlock(x, y, z).getCollisionBoundingBoxFromPool(world, x, y, z) == null || world.isAirBlock(x, y, z); } private static boolean isSolidEnough(World world, int x, int y, int z) { Block block = world.getBlock(x, y, z); AxisAlignedBB aabb = block.getCollisionBoundingBoxFromPool(world, x, y, z); if (aabb == null) return false; double dx = aabb.maxX - aabb.minX; double dy = aabb.maxY - aabb.minY; double dz = aabb.maxZ - aabb.minZ; return (dx >= 0.5) && (dy >= 0.5) && (dz >= 0.5); } private static boolean isChestplate(ItemStack stack) { if (stack == null) return false; Item item = stack.getItem(); if (item instanceof ItemSleepingBag) return false; if (item instanceof ItemArmor) { ItemArmor armorItem = (ItemArmor)item; return armorItem.armorType == ARMOR_CHESTPIECE_TYPE; } return false; } private static Integer getReturnSlot(NBTTagCompound tag) { if (tag.hasKey(TAG_SLOT, Constants.NBT.TAG_ANY_NUMERIC)) { int slot = tag.getInteger(TAG_SLOT); if (slot < 9 && slot >= 0) return slot; } return null; } private static boolean tryReturnToSlot(EntityPlayer player, ItemStack sleepingBag) { NBTTagCompound tag = ItemUtils.getItemTag(sleepingBag); final Integer returnSlot = getReturnSlot(tag); tag.removeTag(TAG_SLOT); if (returnSlot == null) { setChestpieceSlot(player, null); return false; } final ItemStack possiblyArmor = player.inventory.mainInventory[returnSlot]; if (isChestplate(possiblyArmor)) { setChestpieceSlot(player, possiblyArmor); } else { setChestpieceSlot(player, null); if (possiblyArmor != null) return false; } player.inventory.setInventorySlotContents(returnSlot, sleepingBag); return true; } private static void getOutOfSleepingBag(EntityPlayer player) { ItemStack stack = getChestpieceSlot(player); if (isSleepingBag(stack)) { if (!tryReturnToSlot(player, stack)) { if (!player.inventory.addItemStackToInventory(stack)) { BlockUtils.dropItemStackInWorld(player.worldObj, player.posX, player.posY, player.posZ, stack); } } } } private static void storeOriginalSpawn(EntityPlayer player, NBTTagCompound tag) { ChunkCoordinates spawn = player.getBedLocation(player.worldObj.provider.dimensionId); if (spawn != null) tag.setTag(TAG_SPAWN, TagUtils.store(spawn)); } private static void restoreOriginalSpawn(EntityPlayer player, NBTTagCompound tag) { if (tag.hasKey(TAG_SPAWN)) { ChunkCoordinates coords = TagUtils.readCoord(tag.getCompoundTag(TAG_SPAWN)).asChunkCoordinate(); player.setSpawnChunk(coords, false, player.worldObj.provider.dimensionId); tag.removeTag(TAG_SPAWN); } } private static void storeOriginalPosition(Entity e, NBTTagCompound tag) { tag.setTag(TAG_POSITION, TagUtils.store(e.posX, e.posY, e.posZ)); } private static void restoreOriginalPosition(Entity e, NBTTagCompound tag) { if (tag.hasKey(TAG_POSITION)) { Vec3 position = TagUtils.readVec(tag.getCompoundTag(TAG_POSITION)); e.setPosition(position.xCoord, position.yCoord, position.zCoord); tag.removeTag(TAG_POSITION); } } public static boolean isWearingSleepingBag(EntityPlayer player) { ItemStack armor = getChestpieceSlot(player); return isSleepingBag(armor); } private static boolean isSleepingBag(ItemStack armor) { return armor != null && armor.getItem() instanceof ItemSleepingBag; } private static ItemStack setChestpieceSlot(EntityPlayer player, ItemStack chestpiece) { return player.inventory.armorInventory[ARMOR_CHESTPIECE_SLOT] = chestpiece; } private static ItemStack getChestpieceSlot(EntityPlayer player) { return player.inventory.armorInventory[ARMOR_CHESTPIECE_SLOT]; } }