package openblocks.common.tileentity; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import java.util.List; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.command.IEntitySelector; import net.minecraft.entity.Entity; import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.init.Items; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.ForgeDirection; import openblocks.OpenBlocks; import openblocks.OpenBlocks.Blocks; import openmods.Log; import openmods.api.IAddAwareTile; import openmods.api.INeighbourAwareTile; import openmods.api.ISurfaceAttachment; import openmods.fakeplayer.FakePlayerPool; import openmods.fakeplayer.FakePlayerPool.PlayerUserReturning; import openmods.fakeplayer.OpenModsFakePlayer; import openmods.reflection.SafeClassLoad; import openmods.sync.SyncableBoolean; import openmods.tileentity.SyncedTileEntity; import openmods.utils.BlockUtils; import openmods.utils.EntityUtils; public class TileEntityTarget extends SyncedTileEntity implements ISurfaceAttachment, INeighbourAwareTile, IAddAwareTile { private int strength = 0; private int tickCounter = -1; private SyncableBoolean active; private final static SafeClassLoad FLANS_BULLET = SafeClassLoad.create("com.flansmod.common.guns.EntityBullet"); public final static Set<Class<?>> EXTRA_PROJECTILE_CLASSES = Sets.newHashSet(); private static void addClass(SafeClassLoad cls) { if (cls.tryLoad()) EXTRA_PROJECTILE_CLASSES.add(cls.get()); else Log.debug("Class %s not found, skipping target path prediction from FlansMod", cls.clsName); } static { addClass(FLANS_BULLET); } private final static IEntitySelector PROJECTILE_SELECTOR = new IEntitySelector() { @Override public boolean isEntityApplicable(Entity p_82704_1_) { return EXTRA_PROJECTILE_CLASSES.contains(p_82704_1_.getClass()); } }; public TileEntityTarget() {} @Override protected void createSyncedFields() { active = new SyncableBoolean(); } @Override public void updateEntity() { super.updateEntity(); if (!worldObj.isRemote) predictOtherProjectiles(); tickCounter--; if (tickCounter == 0) { tickCounter = -1; strength = 0; worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, OpenBlocks.Blocks.target); } } private void predictOtherProjectiles() { @SuppressWarnings("unchecked") List<Entity> projectiles = worldObj.selectEntitiesWithinAABB(Entity.class, getBB().expand(10, 10, 10), PROJECTILE_SELECTOR); for (Entity projectile : projectiles) { MovingObjectPosition hit = EntityUtils.raytraceEntity(projectile); if (BlockUtils.isBlockHit(hit, this)) { Blocks.target.onTargetHit(worldObj, xCoord, yCoord, zCoord, hit.hitVec); } } } public void setEnabled(boolean en) { active.set(en); } public boolean isEnabled() { return active.get(); } public float getTargetRotation() { return isEnabled()? 0 : -(float)(Math.PI / 2); } public int getStrength() { return strength; } public void setStrength(int strength) { this.strength = strength; tickCounter = 10; worldObj.notifyBlocksOfNeighborChange(xCoord, yCoord, zCoord, OpenBlocks.Blocks.target); } @Override public ForgeDirection getSurfaceDirection() { return ForgeDirection.DOWN; } @Override @SideOnly(Side.CLIENT) public void prepareForInventoryRender(Block block, int metadata) { super.prepareForInventoryRender(block, metadata); setEnabled(true); } @Override public void onNeighbourChanged(Block block) { updateRedstone(); } @Override public void onAdded() { updateRedstone(); } private void updateRedstone() { if (!(worldObj instanceof WorldServer)) return; WorldServer world = (WorldServer)worldObj; boolean isPowered = worldObj.isBlockIndirectlyGettingPowered(xCoord, yCoord, zCoord); if (isPowered != isEnabled()) { dropArrowsAsItems(world); worldObj.playSoundEffect(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5, isPowered? "openblocks:target.open" : "openblocks:target.close", 0.5f, 1.0f); setEnabled(isPowered); sync(); } } private void dropArrowsAsItems(WorldServer world) { final AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(xCoord - 0.1, yCoord - 0.1, zCoord - 0.1, xCoord + 1.1, yCoord + 1.1, zCoord + 1.1); @SuppressWarnings("unchecked") final List<EntityArrow> arrows = worldObj.getEntitiesWithinAABB(EntityArrow.class, aabb); final List<ItemStack> drops = Lists.newArrayList(); int failed = FakePlayerPool.instance.executeOnPlayer(world, new PlayerUserReturning<Integer>() { @Override public Integer usePlayer(OpenModsFakePlayer fakePlayer) { int failed = 0; for (EntityArrow arrow : arrows) { try { arrow.onCollideWithPlayer(fakePlayer); } catch (Throwable t) { Log.warn(t, "Failed to collide arrow %s with fake player, returing vanilla one", arrow); failed++; } } IInventory inventory = fakePlayer.inventory; for (int i = 0; i < inventory.getSizeInventory(); i++) { ItemStack stack = inventory.getStackInSlot(i); if (stack != null) { drops.add(stack); inventory.setInventorySlotContents(i, null); } } return failed; } }); for (ItemStack drop : drops) BlockUtils.dropItemStackInWorld(worldObj, xCoord, yCoord, zCoord, drop); if (failed > 0) BlockUtils.dropItemStackInWorld(worldObj, xCoord, yCoord, zCoord, new ItemStack(Items.arrow, failed)); } }