package slimeknights.tconstruct.tools.melee.item; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.item.EnumAction; import net.minecraft.item.IItemPropertyGetter; import net.minecraft.item.ItemStack; import net.minecraft.util.ActionResult; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import java.util.List; import javax.annotation.Nonnull; import slimeknights.tconstruct.common.TinkerNetwork; import slimeknights.tconstruct.library.materials.Material; import slimeknights.tconstruct.library.tinkering.Category; import slimeknights.tconstruct.library.tinkering.PartMaterialType; import slimeknights.tconstruct.library.tools.TinkerToolCore; import slimeknights.tconstruct.library.tools.ToolNBT; import slimeknights.tconstruct.library.utils.ToolHelper; import slimeknights.tconstruct.tools.TinkerTools; import slimeknights.tconstruct.tools.common.network.EntityMovementChangePacket; // BattleSign Ability: Blocks more damage and can reflect projectiles. The ultimate defensive weapon. public class BattleSign extends TinkerToolCore { public BattleSign() { super(PartMaterialType.handle(TinkerTools.toolRod), PartMaterialType.head(TinkerTools.signHead)); addCategory(Category.WEAPON); this.addPropertyOverride(new ResourceLocation("blocking"), new IItemPropertyGetter() { @Override @SideOnly(Side.CLIENT) public float apply(@Nonnull ItemStack stack, World worldIn, EntityLivingBase entityIn) { return entityIn != null && entityIn.isHandActive() && entityIn.getActiveItemStack() == stack ? 1.0F : 0.0F; } }); } @Override public double attackSpeed() { return 1.2; } @Override public float damagePotential() { return 0.86f; } @Nonnull @Override public EnumAction getItemUseAction(ItemStack stack) { return EnumAction.BLOCK; } @Override public int getMaxItemUseDuration(ItemStack stack) { return 72000; } @Nonnull @Override public ActionResult<ItemStack> onItemRightClick(@Nonnull ItemStack itemStackIn, World worldIn, EntityPlayer playerIn, EnumHand hand) { if(!ToolHelper.isBroken(itemStackIn)) { playerIn.setActiveHand(hand); return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, itemStackIn); } return new ActionResult<ItemStack>(EnumActionResult.FAIL, itemStackIn); } // Extra damage reduction when blocking with a battlesign @SubscribeEvent(priority = EventPriority.LOW) // lower priority so we get called later since we change tool NBT public void reducedDamageBlocked(LivingHurtEvent event) { // don't affect unblockable or magic damage or explosion damage // projectiles are handled in LivingAttackEvent if(event.getSource().isUnblockable() || event.getSource().isMagicDamage() || event.getSource().isExplosion() || event.getSource().isProjectile() || event.isCanceled()) { return; } if(!shouldBlockDamage(event.getEntityLiving())) { return; } EntityPlayer player = (EntityPlayer) event.getEntityLiving(); ItemStack battlesign = player.getActiveItemStack(); // got hit by something: reduce damage int damage = event.getAmount() < 2f ? 1 : Math.round(event.getAmount() / 2f); // reduce damage. After this event the damage will be halved again because we're blocking so we have to factor this in event.setAmount(event.getAmount() * 0.7f); // reflect damage if(event.getSource().getEntity() != null) { event.getSource().getEntity().attackEntityFrom(DamageSource.causeThornsDamage(player), event.getAmount() / 2f); damage = damage * 3 / 2; } ToolHelper.damageTool(battlesign, damage, player); } @SubscribeEvent public void reflectProjectiles(LivingAttackEvent event) { // only blockable projectile damage if(event.getSource().isUnblockable() || !event.getSource().isProjectile() || event.getSource().getSourceOfDamage() == null) { return; } if(!shouldBlockDamage(event.getEntityLiving())) { return; } EntityPlayer player = (EntityPlayer) event.getEntityLiving(); ItemStack battlesign = player.getActiveItemStack(); // ensure the player is looking at the projectile (aka not getting shot into the back) Entity projectile = event.getSource().getSourceOfDamage(); Vec3d motion = new Vec3d(projectile.motionX, projectile.motionY, projectile.motionZ); Vec3d look = player.getLookVec(); // this gives a factor of how much we're looking at the incoming arrow double strength = -look.dotProduct(motion.normalize()); // we're looking away. oh no. if(strength < 0.1) { return; } // caught that bastard! block it! event.setCanceled(true); // and return it to the sender // calc speed of the projectile double speed = projectile.motionX * projectile.motionX + projectile.motionY * projectile.motionY + projectile.motionZ * projectile.motionZ; speed = Math.sqrt(speed); speed += 0.2f; // we add a bit speed // and redirect it to where the player is looking projectile.motionX = look.xCoord * speed; projectile.motionY = look.yCoord * speed; projectile.motionZ = look.zCoord * speed; projectile.rotationYaw = (float) (Math.atan2(projectile.motionX, projectile.motionZ) * 180.0D / Math.PI); projectile.rotationPitch = (float) (Math.atan2(projectile.motionY, speed) * 180.0D / Math.PI); // notify clients from change, otherwise people will get veeeery confused TinkerNetwork.sendToAll(new EntityMovementChangePacket(projectile)); // special treatement for arrows if(projectile instanceof EntityArrow) { ((EntityArrow) projectile).shootingEntity = player; // the inverse is done when the event is cancelled in arrows etc. // we reverse it so it has no effect. yay projectile.motionX /= -0.10000000149011612D; projectile.motionY /= -0.10000000149011612D; projectile.motionZ /= -0.10000000149011612D; } // use durability equal to the damage prevented ToolHelper.damageTool(battlesign, (int) event.getAmount(), player); } protected boolean shouldBlockDamage(Entity entity) { // hit entity is a player? if(!(entity instanceof EntityPlayer)) { return false; } EntityPlayer player = (EntityPlayer) entity; // needs to be blocking with a battlesign if(!player.isActiveItemStackBlocking() || player.getActiveItemStack().getItem() != this) { return false; } // broken battlesign. return !ToolHelper.isBroken(player.getActiveItemStack()); } @Override public ToolNBT buildTagData(List<Material> materials) { return buildDefaultTag(materials); } }