package slimeknights.tconstruct.library.tools.ranged;
import com.google.common.collect.Multimap;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.monster.EntityEnderman;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EntityDamageSource;
import net.minecraft.util.EntityDamageSourceIndirect;
import net.minecraft.world.World;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import slimeknights.tconstruct.library.Util;
import slimeknights.tconstruct.library.materials.Material;
import slimeknights.tconstruct.library.tinkering.PartMaterialType;
import slimeknights.tconstruct.library.tools.ProjectileNBT;
import slimeknights.tconstruct.library.tools.TinkerToolCore;
import slimeknights.tconstruct.library.utils.ToolHelper;
import slimeknights.tconstruct.library.utils.TooltipBuilder;
import slimeknights.tconstruct.tools.traits.TraitEnderference;
/**
* This class is a tool that has ammo.
* Durability works like usually, but ammo is abstracted on top of durability.
* So durability controls the interaction with materials, and ammo-ratio controls the interaction betweer durability and ammo
*/
public abstract class ProjectileCore extends TinkerToolCore implements IProjectile, IAmmo {
public static final String DAMAGE_TYPE_PROJECTILE = "arrow";
protected int durabilityPerAmmo;
public ProjectileCore(PartMaterialType... requiredComponents) {
super(requiredComponents);
durabilityPerAmmo = 10;
}
/* Ammo Handling */
protected void setDurabilityPerAmmo(int durabilityPerAmmo) {
this.durabilityPerAmmo = durabilityPerAmmo;
}
public int getDurabilityPerAmmo() {
return durabilityPerAmmo;
}
@Override
public double getDurabilityForDisplay(ItemStack stack) {
// this is inverted, we return how DAMAGED the tool is, not how healthy
return (double) (getMaxAmmo(stack) - getCurrentAmmo(stack)) / (double) getMaxAmmo(stack);
}
@Override
public boolean showDurabilityBar(ItemStack stack) {
return getMaxAmmo(stack) != getCurrentAmmo(stack) && super.showDurabilityBar(stack);
}
@Override
public int getCurrentAmmo(ItemStack stack) {
return ToolHelper.getCurrentDurability(stack) / durabilityPerAmmo;
}
@Override
public int getMaxAmmo(ItemStack stack) {
return ToolHelper.getMaxDurability(stack) / durabilityPerAmmo;
}
@Override
public void setAmmo(int count, ItemStack stack) {
stack.setItemDamage(count * durabilityPerAmmo);
}
@Override
public boolean addAmmo(ItemStack stack, EntityLivingBase player) {
int ammo = getCurrentAmmo(stack);
if(ammo < getMaxAmmo(stack)) {
ToolHelper.healTool(stack, durabilityPerAmmo, null);
return true;
}
else {
return false;
}
}
@Override
public boolean useAmmo(ItemStack stack, @Nullable EntityLivingBase player) {
int ammo = getCurrentAmmo(stack);
if(ammo > 0) {
ToolHelper.damageTool(stack, durabilityPerAmmo, player);
int newAmmo = getCurrentAmmo(stack);
if(newAmmo <= 0) {
ToolHelper.breakTool(stack, player);
}
// in case we're creative or a trait like obsidian's prevented the damage
return newAmmo < ammo;
}
else {
return false;
}
}
/* Tool stuff */
protected ItemStack getProjectileStack(ItemStack itemStack, World world, EntityPlayer player, boolean usedAmmo) {
ItemStack reference = itemStack.copy();
reference.stackSize = 1;
setAmmo(1, reference);
// prevent a positive feedback loop with picking up ammo + durability retaining modifiers like reinforced
if(!player.capabilities.isCreativeMode && !world.isRemote && !usedAmmo) {
reference.stackSize = 0;
setAmmo(0, reference);
}
// never broken
ToolHelper.unbreakTool(reference);
return reference;
}
@Override
public boolean onLeftClickEntity(ItemStack stack, EntityPlayer player, Entity entity) {
// projectiles behave like regular items
return false;
}
@Override
public double attackSpeed() {
return 100f;
}
@Override
public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase attacker) {
return false;
}
@Override
public boolean dealDamageRanged(ItemStack stack, Entity projectile, EntityLivingBase player, Entity entity, float damage) {
DamageSource damageSource = new EntityDamageSourceIndirect(DAMAGE_TYPE_PROJECTILE, projectile, player).setProjectile();
// friggin vanilla hardcode
if(entity instanceof EntityEnderman && ((EntityEnderman) entity).getActivePotionEffect(TraitEnderference.Enderference) != null) {
damageSource = new DamageSourceProjectileForEndermen(DAMAGE_TYPE_PROJECTILE, projectile, player);
}
return entity.attackEntityFrom(damageSource, damage);
}
@Override
protected String getBrokenTooltip(ItemStack itemStack) {
return Util.translate(TooltipBuilder.LOC_Empty);
}
@Override
public List<String> getInformation(ItemStack stack, boolean detailed) {
TooltipBuilder info = new TooltipBuilder(stack);
info.addAmmo(!detailed);
info.addAttack();
info.addAccuracy();
if(ToolHelper.getFreeModifiers(stack) > 0) {
info.addFreeModifiers();
}
if(detailed) {
info.addModifierInfo();
}
return info.getTooltip();
}
@Nonnull
@Override
public Multimap<String, AttributeModifier> getAttributeModifiers(@Nonnull EntityEquipmentSlot slot, ItemStack stack) {
// no special attributes for ranged weapons
return this.getItemAttributeModifiers(slot);
}
@Override
public Multimap<String, AttributeModifier> getProjectileAttributeModifier(ItemStack stack) {
// return the standard damage map
return super.getAttributeModifiers(EntityEquipmentSlot.MAINHAND, stack);
}
@Override
public abstract ProjectileNBT buildTagData(List<Material> materials);
public static class DamageSourceProjectileForEndermen extends EntityDamageSource {
public final Entity projectile;
public DamageSourceProjectileForEndermen(String damageTypeIn, Entity projectile, Entity damageSourceEntityIn) {
super(damageTypeIn, damageSourceEntityIn);
this.projectile = projectile;
}
@Nullable
@Override
public Entity getSourceOfDamage() {
return projectile;
}
}
}