package slimeknights.tconstruct.tools.traits;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;
import slimeknights.tconstruct.library.modifiers.ModifierAspect;
import slimeknights.tconstruct.library.tools.ToolNBT;
import slimeknights.tconstruct.library.utils.TagUtil;
import slimeknights.tconstruct.library.utils.ToolHelper;
/**
* This trait provides long-term benefits for using the same tool.
* Basically it makes your tool get better over time as you use it.
*
* Detailed explanation: The tool gets a "stat pool" to distribute over time.
* Stats are added to the pool on repairing, mining and attacking.
* Values are dimishing depending on overall stats of the tool. So the growth slows down depending on your overall tool stats.
*/
public class ToolGrowth extends TraitProgressiveStats {
protected static float DURABILITY_COEFFICIENT = 0.04f; // % of amount repaired
protected static float SPEED_INCREMENT = 0.05f; // flat
protected static float ATTACK_INCREMENT = 0.03f; // flat
// how much the value gets increased in one distribution step
protected static int DURABILITY_STEP = 1;
protected static float SPEED_STEP = 0.01f;
protected static float ATTACK_STEP = 0.01f;
public ToolGrowth() {
super("toolgrowth", TextFormatting.WHITE);
this.addAspects(new ModifierAspect.SingleAspect(this));
}
@Override
public boolean isHidden() {
return true;
}
/* Actual logic */
// Distributing stats from the pool
@Override
public void onUpdate(ItemStack tool, World world, Entity entity, int itemSlot, boolean isSelected) {
if(entity instanceof FakePlayer || world.isRemote) {
return;
}
// we only distribute every minute or so
if(random.nextFloat() > 0.0006f) {
return;
}
// we don't update if the player is currently breaking a block because that'd reset it
if(playerIsBreakingBlock(entity)) {
return;
}
// get stat pool
NBTTagCompound root = TagUtil.getTagSafe(tool);
StatNBT pool = getPool(root);
StatNBT bonus = getBonus(root);
ToolNBT data = TagUtil.getToolStats(tool);
// pick one
int choice = random.nextInt(3);
// durability
if(choice == 0) {
if(pool.durability >= DURABILITY_STEP) {
pool.durability -= DURABILITY_STEP;
bonus.durability += DURABILITY_STEP;
data.durability += DURABILITY_STEP;
}
}
// speed
else if(choice == 1) {
if(pool.speed >= SPEED_STEP) {
pool.speed -= SPEED_STEP;
bonus.speed += SPEED_STEP;
data.speed += SPEED_STEP;
}
}
// attack
else if(choice == 2) {
if(pool.attack >= ATTACK_STEP) {
pool.attack -= ATTACK_STEP;
bonus.attack += ATTACK_STEP;
data.attack += ATTACK_STEP;
}
}
// update stats on the tool
TagUtil.setToolTag(tool, data.get());
// write pool and bonus back onto the tool
setBonus(root, bonus);
setPool(root, pool);
}
// Filling the pool with durability
@Override
public void onRepair(ItemStack tool, int amount) {
// read data from tool
NBTTagCompound root = TagUtil.getTagSafe(tool);
StatNBT pool = getPool(root);
int totalDurability = ToolHelper.getDurabilityStat(tool);
float famount = amount;
// cap the amount if it's more than what gets repaired
if(famount > totalDurability - ToolHelper.getCurrentDurability(tool)) {
famount = totalDurability - ToolHelper.getCurrentDurability(tool);
}
// add a bit of random
famount *= 0.975f + random.nextFloat() * 0.05f;
// calculate stats to add to pool. Baseline: 1000 durability
int extra = (int) (calcDimishingReturns(totalDurability, 1000f) * famount * DURABILITY_COEFFICIENT);
pool.durability += 1 + extra;
// write data back onto the tool
setPool(root, pool);
}
// Filling the pool with speed
@Override
public void afterBlockBreak(ItemStack tool, World world, IBlockState state, BlockPos pos, EntityLivingBase player, boolean wasEffective) {
if(player instanceof FakePlayer || world.isRemote) {
return;
}
// 10% chance to gain stats on effective blockbreak
if(!wasEffective || random.nextFloat() > 0.1f) {
return;
}
// read data from tool
NBTTagCompound root = TagUtil.getTagSafe(tool);
StatNBT pool = getPool(root);
float totalSpeed = ToolHelper.getMiningSpeedStat(tool);
// calculate stats to add to pool. Baseline: 5
float extra = calcDimishingReturns(totalSpeed, 5f) * SPEED_INCREMENT;
pool.speed += extra + 0.005f;
// write data back onto the tool
setPool(root, pool);
}
// Filling the pool with damage
@Override
public void afterHit(ItemStack tool, EntityLivingBase player, EntityLivingBase target, float damageDealt, boolean wasCritical, boolean wasHit) {
if(player instanceof FakePlayer || player.getEntityWorld().isRemote) {
return;
}
// 10% chance on hit to gain stats
if(random.nextFloat() > 0.1f) {
return;
}
// read data from tool
NBTTagCompound root = TagUtil.getTagSafe(tool);
StatNBT pool = getPool(root);
float totalSpeed = ToolHelper.getMiningSpeedStat(tool);
// calculate stats to add to pool. Baseline: 10 (= 5 hearts)
float extra = calcDimishingReturns(totalSpeed, 10f) * ATTACK_INCREMENT;
pool.attack += extra + 0.005f;
// write data back onto the tool
setPool(root, pool);
}
// Filling the pool with dimishing returns!
protected float calcDimishingReturns(float value, float baseline) {
return 2f / (1f + (value / baseline) * (value / baseline));
}
}