package slimeknights.toolleveling; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; 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 javax.annotation.Nullable; import slimeknights.tconstruct.library.entity.EntityProjectileBase; import slimeknights.tconstruct.library.events.TinkerToolEvent; import slimeknights.tconstruct.library.modifiers.ModifierAspect; import slimeknights.tconstruct.library.modifiers.ModifierTrait; import slimeknights.tconstruct.library.modifiers.ProjectileModifierTrait; import slimeknights.tconstruct.library.modifiers.TinkerGuiException; import slimeknights.tconstruct.library.tinkering.TinkersItem; import slimeknights.tconstruct.library.tools.ProjectileLauncherNBT; import slimeknights.tconstruct.library.tools.ranged.BowCore; import slimeknights.tconstruct.library.traits.IProjectileTrait; import slimeknights.tconstruct.library.utils.TagUtil; import slimeknights.tconstruct.library.utils.Tags; import slimeknights.tconstruct.library.utils.TinkerUtil; import slimeknights.tconstruct.library.utils.ToolBuilder; import slimeknights.tconstruct.library.utils.ToolHelper; import slimeknights.tconstruct.tools.melee.TinkerMeleeWeapons; import slimeknights.toolleveling.capability.CapabilityDamageXp; import slimeknights.toolleveling.config.Config; public class ModToolLeveling extends ProjectileModifierTrait { public ModToolLeveling() { super("toolleveling", 0xffffff); aspects.clear(); addAspects(new ModifierAspect.DataAspect(this)); MinecraftForge.EVENT_BUS.register(this); } @Override public boolean isHidden() { return true; } @Override public boolean canApplyCustom(ItemStack stack) { return true; } @Override public void applyEffect(NBTTagCompound rootCompound, NBTTagCompound modifierTag) { super.applyEffect(rootCompound, modifierTag); ToolLevelNBT data = getLevelData(modifierTag); // apply bonus modifiers NBTTagCompound toolTag = TagUtil.getToolTag(rootCompound); int modifiers = toolTag.getInteger(Tags.FREE_MODIFIERS) + data.bonusModifiers; toolTag.setInteger(Tags.FREE_MODIFIERS, Math.max(0, modifiers)); TagUtil.setToolTag(rootCompound, toolTag); } /* Actions that award XP */ @Override public void afterBlockBreak(ItemStack tool, World world, IBlockState state, BlockPos pos, EntityLivingBase player, boolean wasEffective) { if(wasEffective && player instanceof EntityPlayer) { addXp(tool, 1, (EntityPlayer) player); } } @Override public void afterHit(ItemStack tool, EntityLivingBase player, EntityLivingBase target, float damageDealt, boolean wasCritical, boolean wasHit) { if(!target.getEntityWorld().isRemote && wasHit && player instanceof EntityPlayer) { // if we killed it the event for distributing xp was already fired and we just do it manually here EntityPlayer entityPlayer = (EntityPlayer) player; if(!target.isEntityAlive()) { addXp(tool, Math.round(damageDealt), entityPlayer); } else if(target.hasCapability(CapabilityDamageXp.CAPABILITY, null)) { target.getCapability(CapabilityDamageXp.CAPABILITY, null).addDamageFromTool(damageDealt, tool, entityPlayer); } } } @Override public void onBlock(ItemStack tool, EntityPlayer player, LivingHurtEvent event) { if(player != null && !player.world.isRemote && player.getActiveItemStack() == tool) { int xp = Math.round(event.getAmount()); addXp(tool, xp, player); } } @SubscribeEvent public void onMattock(TinkerToolEvent.OnMattockHoe event) { addXp(event.itemStack, 1, event.player); } @SubscribeEvent public void onScythe(TinkerToolEvent.OnScytheHarvest event) { if(!event.isCanceled()) { addXp(event.itemStack, 1, event.player); } } @SubscribeEvent public void onPath(TinkerToolEvent.OnShovelMakePath event) { addXp(event.itemStack, 1, event.player); } @SubscribeEvent(priority = EventPriority.LOWEST, receiveCanceled = true) public void onLivingHurt(LivingAttackEvent event) { // if it's cancelled it got handled by the battlesign (or something else. but it's a prerequisite.) if(!event.isCanceled()) { return; } if(event.getSource().isUnblockable() || !event.getSource().isProjectile() || event.getSource().getTrueSource() == null) { return; } // hit entity is a player? if(!(event.getEntity() instanceof EntityPlayer)) { return; } EntityPlayer player = (EntityPlayer) event.getEntity(); // needs to be blocking with a battlesign if(!player.isActiveItemStackBlocking() || player.getActiveItemStack().getItem() != TinkerMeleeWeapons.battleSign) { return; } // broken battlesign. if(ToolHelper.isBroken(player.getActiveItemStack())) { return; } // at this point we duplicated all the logic if the battlesign should reflect a projectile.. bleh. int xp = Math.max(1, Math.round(event.getAmount())); addXp(player.getActiveItemStack(), xp, player); } /* XP Handling */ public void addXp(ItemStack tool, int amount, EntityPlayer player) { NBTTagList tagList = TagUtil.getModifiersTagList(tool); int index = TinkerUtil.getIndexInCompoundList(tagList, identifier); NBTTagCompound modifierTag = tagList.getCompoundTagAt(index); ToolLevelNBT data = getLevelData(modifierTag); data.xp += amount; // is max level? if(!Config.canLevelUp(data.level)) { return; } int xpForLevelup = getXpForLevelup(data.level, tool); boolean leveledUp = false; // check for levelup if(data.xp >= xpForLevelup) { data.xp -= xpForLevelup; data.level++; data.bonusModifiers++; leveledUp = true; } data.write(modifierTag); //tagList.set(index, modifierTag); TagUtil.setModifiersTagList(tool, tagList); if(leveledUp) { this.apply(tool); if(!player.world.isRemote) { // for some reason the proxy is messed up. cba to fix now TinkerToolLeveling.proxy.playLevelupDing(player); TinkerToolLeveling.proxy.sendLevelUpMessage(data.level, tool, player); } try { NBTTagCompound rootTag = TagUtil.getTagSafe(tool); ToolBuilder.rebuildTool(rootTag, (TinkersItem) tool.getItem()); tool.setTagCompound(rootTag); } catch(TinkerGuiException e) { // this should never happen e.printStackTrace(); } } } public int getXpForLevelup(int level, ItemStack tool) { if(level <= 1) { return Config.getBaseXpForTool(tool.getItem()); } return (int) ((float) getXpForLevelup(level - 1, tool) * Config.getLevelMultiplier()); } private ToolLevelNBT getLevelData(ItemStack itemStack) { return getLevelData(TinkerUtil.getModifierTag(itemStack, getModifierIdentifier())); } private ToolLevelNBT getLevelData(NBTTagCompound modifierNBT) { return new ToolLevelNBT(modifierNBT); } @Override public void afterHit(EntityProjectileBase projectile, World world, ItemStack ammoStack, EntityLivingBase attacker, Entity target, double impactSpeed) { if(impactSpeed > 0.4f && attacker instanceof EntityPlayer) { ItemStack launcher = projectile.tinkerProjectile.getLaunchingStack(); if(launcher.getItem() instanceof BowCore) { double drawTime = ((BowCore) launcher.getItem()).getDrawTime(); double drawSpeed = ProjectileLauncherNBT.from(launcher).drawSpeed; double drawTimeInSeconds = 1d / (20d * drawSpeed/drawTime); // we award 5 xp per 1s draw time int xp = MathHelper.ceil((5d * drawTimeInSeconds)); this.addXp(launcher, xp, (EntityPlayer) attacker); } } } }