/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. * * Applied Energistics 2 is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Applied Energistics 2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Applied Energistics 2. If not, see . */ package appeng.items.tools.powered; import java.util.List; import javax.annotation.Nullable; import net.minecraft.block.BlockState; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.passive.SheepEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.*; import net.minecraft.util.math.*; import net.minecraft.util.text.ITextComponent; import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.items.IItemHandler; import appeng.api.AEApi; import appeng.api.config.Actionable; import appeng.api.config.FuzzyMode; import appeng.api.config.Upgrades; import appeng.api.features.AEFeature; import appeng.api.implementations.items.IStorageCell; import appeng.api.storage.ICellInventoryHandler; import appeng.api.storage.IStorageChannel; import appeng.api.storage.channels.IItemStorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.api.util.AEColor; import appeng.api.util.DimensionalCoord; import appeng.core.AEConfig; import appeng.core.AELog; import appeng.core.AppEng; import appeng.core.localization.PlayerMessages; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketMatterCannon; import appeng.hooks.TickHandler; import appeng.hooks.TickHandler.PlayerColor; import appeng.items.contents.CellConfig; import appeng.items.contents.CellUpgrades; import appeng.items.misc.ItemPaintBall; import appeng.items.tools.powered.powersink.AEBasePoweredItem; import appeng.me.helpers.PlayerSource; import appeng.tile.misc.TilePaint; import appeng.util.LookDirection; import appeng.util.Platform; public class ToolMatterCannon extends AEBasePoweredItem implements IStorageCell { public ToolMatterCannon(Item.Properties props) { super(AEConfig.instance().getMatterCannonBattery(), props); } @OnlyIn(Dist.CLIENT) @Override public void addInformation(final ItemStack stack, final World world, final List lines, final ITooltipFlag advancedTooltips) { super.addInformation(stack, world, lines, advancedTooltips); final ICellInventoryHandler cdi = AEApi.instance().registries().cell().getCellInventory(stack, null, AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)); AEApi.instance().client().addCellInformation(cdi, lines); } @Override public ActionResult onItemRightClick(final World w, final PlayerEntity p, final @Nullable Hand hand) { if (this.getAECurrentPower(p.getHeldItem(hand)) > 1600) { int shots = 1; final CellUpgrades cu = (CellUpgrades) this.getUpgradesInventory(p.getHeldItem(hand)); if (cu != null) { shots += cu.getInstalledUpgrades(Upgrades.SPEED); } final ICellInventoryHandler inv = AEApi.instance().registries().cell().getCellInventory( p.getHeldItem(hand), null, AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)); if (inv != null) { final IItemList itemList = inv.getAvailableItems( AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class).createList()); IAEItemStack req = itemList.getFirstItem(); if (req instanceof IAEItemStack) { shots = Math.min(shots, (int) req.getStackSize()); for (int sh = 0; sh < shots; sh++) { IAEItemStack aeAmmo = req.copy(); this.extractAEPower(p.getHeldItem(hand), 1600, Actionable.MODULATE); if (Platform.isClient()) { return new ActionResult<>(ActionResultType.SUCCESS, p.getHeldItem(hand)); } aeAmmo.setStackSize(1); final ItemStack ammo = aeAmmo.createItemStack(); if (ammo == null) { return new ActionResult<>(ActionResultType.SUCCESS, p.getHeldItem(hand)); } aeAmmo = inv.extractItems(aeAmmo, Actionable.MODULATE, new PlayerSource(p, null)); if (aeAmmo == null) { return new ActionResult<>(ActionResultType.SUCCESS, p.getHeldItem(hand)); } final LookDirection dir = Platform.getPlayerRay(p); final Vec3d Vec3d = dir.getA(); final Vec3d Vec3d1 = dir.getB(); final Vec3d direction = Vec3d1.subtract(Vec3d); direction.normalize(); final double d0 = Vec3d.x; final double d1 = Vec3d.y; final double d2 = Vec3d.z; final float penetration = AEApi.instance().registries().matterCannon().getPenetration(ammo); // 196.96655f; if (penetration <= 0) { final ItemStack type = aeAmmo.asItemStackRepresentation(); if (type.getItem() instanceof ItemPaintBall) { this.shootPaintBalls(type, w, p, Vec3d, Vec3d1, direction, d0, d1, d2); } return new ActionResult<>(ActionResultType.SUCCESS, p.getHeldItem(hand)); } else { this.standardAmmo(penetration, w, p, Vec3d, Vec3d1, direction, d0, d1, d2); } } } else { if (Platform.isServer()) { p.sendMessage(PlayerMessages.AmmoDepleted.get()); } return new ActionResult<>(ActionResultType.SUCCESS, p.getHeldItem(hand)); } } } return new ActionResult<>(ActionResultType.FAIL, p.getHeldItem(hand)); } private void shootPaintBalls(final ItemStack type, final World w, final PlayerEntity p, final Vec3d Vec3d, final Vec3d Vec3d1, final Vec3d direction, final double d0, final double d1, final double d2) { final AxisAlignedBB bb = new AxisAlignedBB(Math.min(Vec3d.x, Vec3d1.x), Math.min(Vec3d.y, Vec3d1.y), Math.min(Vec3d.z, Vec3d1.z), Math.max(Vec3d.x, Vec3d1.x), Math.max(Vec3d.y, Vec3d1.y), Math.max(Vec3d.z, Vec3d1.z)).grow(16, 16, 16); Entity entity = null; Vec3d entityIntersection = null; final List list = w.getEntitiesWithinAABBExcludingEntity(p, bb); double closest = 9999999.0D; for (int l = 0; l < list.size(); ++l) { final Entity entity1 = (Entity) list.get(l); if (!entity1.isAlive() && entity1 != p && !(entity1 instanceof ItemEntity)) { if (entity1.isAlive()) { // prevent killing / flying of mounts. if (entity1.isRidingOrBeingRiddenBy(p)) { continue; } final float f1 = 0.3F; final AxisAlignedBB boundingBox = entity1.getBoundingBox().grow(f1, f1, f1); final Vec3d intersection = boundingBox.rayTrace(Vec3d, Vec3d1).orElse(null); if (intersection != null) { final double nd = Vec3d.squareDistanceTo(intersection); if (nd < closest) { entity = entity1; entityIntersection = intersection; closest = nd; } } } } } RayTraceContext rayTraceContext = new RayTraceContext(Vec3d, Vec3d1, RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.NONE, p); RayTraceResult pos = w.rayTraceBlocks(rayTraceContext); final Vec3d vec = new Vec3d(d0, d1, d2); if (entity != null && pos.getType() != RayTraceResult.Type.MISS && pos.getHitVec().squareDistanceTo(vec) > closest) { pos = new EntityRayTraceResult(entity, entityIntersection); } else if (entity != null && pos.getType() == RayTraceResult.Type.MISS) { pos = new EntityRayTraceResult(entity, entityIntersection); } try { AppEng.proxy.sendToAllNearExcept(null, d0, d1, d2, 128, w, new PacketMatterCannon(d0, d1, d2, (float) direction.x, (float) direction.y, (float) direction.z, (byte) (pos.getType() == RayTraceResult.Type.MISS ? 32 : pos.getHitVec().squareDistanceTo(vec) + 1))); } catch (final Exception err) { AELog.debug(err); } if (pos.getType() != RayTraceResult.Type.MISS && type != null && type.getItem() instanceof ItemPaintBall) { final ItemPaintBall ipb = (ItemPaintBall) type.getItem(); final AEColor col = ipb.getColor(); // boolean lit = ipb.isLumen( type ); if (pos instanceof EntityRayTraceResult) { EntityRayTraceResult entityResult = (EntityRayTraceResult) pos; Entity entityHit = entityResult.getEntity(); final int id = entityHit.getEntityId(); final PlayerColor marker = new PlayerColor(id, col, 20 * 30); TickHandler.INSTANCE.getPlayerColors().put(id, marker); if (entityHit instanceof SheepEntity) { final SheepEntity sh = (SheepEntity) entityHit; sh.setFleeceColor(col.dye); } entityHit.attackEntityFrom(DamageSource.causePlayerDamage(p), 0); NetworkHandler.instance().sendToAll(marker.getPacket()); } else if (pos instanceof BlockRayTraceResult) { BlockRayTraceResult blockResult = (BlockRayTraceResult) pos; final Direction side = blockResult.getFace(); final BlockPos hitPos = blockResult.getPos().offset(side); if (!Platform.hasPermissions(new DimensionalCoord(w, hitPos), p)) { return; } final BlockState whatsThere = w.getBlockState(hitPos); if (whatsThere.getMaterial().isReplaceable() && w.isAirBlock(hitPos)) { AEApi.instance().definitions().blocks().paint().maybeBlock().ifPresent(paintBlock -> { w.setBlockState(hitPos, paintBlock.getDefaultState(), 3); }); } final TileEntity te = w.getTileEntity(hitPos); if (te instanceof TilePaint) { final Vec3d hp = pos.getHitVec().subtract(hitPos.getX(), hitPos.getY(), hitPos.getZ()); ((TilePaint) te).addBlot(type, side.getOpposite(), hp); } } } } private void standardAmmo(float penetration, final World w, final PlayerEntity p, final Vec3d Vec3d, final Vec3d Vec3d1, final Vec3d direction, final double d0, final double d1, final double d2) { boolean hasDestroyed = true; while (penetration > 0 && hasDestroyed) { hasDestroyed = false; final AxisAlignedBB bb = new AxisAlignedBB(Math.min(Vec3d.x, Vec3d1.x), Math.min(Vec3d.y, Vec3d1.y), Math.min(Vec3d.z, Vec3d1.z), Math.max(Vec3d.x, Vec3d1.x), Math.max(Vec3d.y, Vec3d1.y), Math.max(Vec3d.z, Vec3d1.z)).grow(16, 16, 16); Entity entity = null; Vec3d entityIntersection = null; final List list = w.getEntitiesWithinAABBExcludingEntity(p, bb); double closest = 9999999.0D; for (int l = 0; l < list.size(); ++l) { final Entity entity1 = (Entity) list.get(l); if (entity1.isAlive() && entity1 != p && !(entity1 instanceof ItemEntity)) { if (entity1.isAlive()) { // prevent killing / flying of mounts. if (entity1.isRidingOrBeingRiddenBy(p)) { continue; } final float f1 = 0.3F; final AxisAlignedBB boundingBox = entity1.getBoundingBox().grow(f1, f1, f1); final Vec3d intersection = boundingBox.rayTrace(Vec3d, Vec3d1).orElse(null); if (intersection != null) { final double nd = Vec3d.squareDistanceTo(intersection); if (nd < closest) { entity = entity1; entityIntersection = intersection; closest = nd; } } } } } RayTraceContext rayTraceContext = new RayTraceContext(Vec3d, Vec3d1, RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.NONE, p); final Vec3d vec = new Vec3d(d0, d1, d2); RayTraceResult pos = w.rayTraceBlocks(rayTraceContext); if (entity != null && pos.getType() != RayTraceResult.Type.MISS && pos.getHitVec().squareDistanceTo(vec) > closest) { pos = new EntityRayTraceResult(entity, entityIntersection); } else if (entity != null && pos.getType() == RayTraceResult.Type.MISS) { pos = new EntityRayTraceResult(entity, entityIntersection); } try { AppEng.proxy.sendToAllNearExcept(null, d0, d1, d2, 128, w, new PacketMatterCannon(d0, d1, d2, (float) direction.x, (float) direction.y, (float) direction.z, (byte) (pos.getType() == RayTraceResult.Type.MISS ? 32 : pos.getHitVec().squareDistanceTo(vec) + 1))); } catch (final Exception err) { AELog.debug(err); } if (pos.getType() != RayTraceResult.Type.MISS) { final DamageSource dmgSrc = new EntityDamageSource("matter_cannon", p); if (pos instanceof EntityRayTraceResult) { EntityRayTraceResult entityResult = (EntityRayTraceResult) pos; Entity entityHit = entityResult.getEntity(); final int dmg = (int) Math.ceil(penetration / 20.0f); if (entityHit instanceof LivingEntity) { final LivingEntity el = (LivingEntity) entityHit; penetration -= dmg; el.knockBack(p, 0, -direction.x, -direction.z); // el.knockBack( p, 0, Vec3d.x, // Vec3d.z ); el.attackEntityFrom(dmgSrc, dmg); if (!el.isAlive()) { hasDestroyed = true; } } else if (entityHit instanceof ItemEntity) { hasDestroyed = true; entityHit.remove(); } else if (entityHit.attackEntityFrom(dmgSrc, dmg)) { hasDestroyed = true; } } else if (pos instanceof BlockRayTraceResult) { BlockRayTraceResult blockResult = (BlockRayTraceResult) pos; if (!AEConfig.instance().isFeatureEnabled(AEFeature.MASS_CANNON_BLOCK_DAMAGE)) { penetration = 0; } else { BlockPos blockPos = blockResult.getPos(); final BlockState bs = w.getBlockState(blockPos); final float hardness = bs.getBlockHardness(w, blockPos) * 9.0f; if (hardness >= 0.0) { if (penetration > hardness && Platform.hasPermissions(new DimensionalCoord(w, blockPos), p)) { hasDestroyed = true; penetration -= hardness; penetration *= 0.60; w.destroyBlock(blockPos, true); } } } } } } } @Override public boolean isEditable(final ItemStack is) { return true; } @Override public IItemHandler getUpgradesInventory(final ItemStack is) { return new CellUpgrades(is, 4); } @Override public IItemHandler getConfigInventory(final ItemStack is) { return new CellConfig(is); } @Override public FuzzyMode getFuzzyMode(final ItemStack is) { final String fz = is.getOrCreateTag().getString("FuzzyMode"); try { return FuzzyMode.valueOf(fz); } catch (final Throwable t) { return FuzzyMode.IGNORE_ALL; } } @Override public void setFuzzyMode(final ItemStack is, final FuzzyMode fzMode) { is.getOrCreateTag().putString("FuzzyMode", fzMode.name()); } @Override public int getBytes(final ItemStack cellItem) { return 512; } @Override public int getBytesPerType(final ItemStack cellItem) { return 8; } @Override public int getTotalTypes(final ItemStack cellItem) { return 1; } @Override public boolean isBlackListed(final ItemStack cellItem, final IAEItemStack requestedAddition) { final float pen = AEApi.instance().registries().matterCannon() .getPenetration(requestedAddition.createItemStack()); if (pen > 0) { return false; } if (requestedAddition.getItem() instanceof ItemPaintBall) { return false; } return true; } @Override public boolean storableInStorageCell() { return true; } @Override public boolean isStorageCell(final ItemStack i) { return true; } @Override public double getIdleDrain() { return 0.5; } @Override public IStorageChannel getChannel() { return AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class); } }