/** Copyright (C) <2015> <coolAlias> This file is part of coolAlias' Zelda Sword Skills Minecraft Mod; as such, you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package zeldaswordskills.skills.sword; import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.util.StatCollector; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import zeldaswordskills.api.item.ArmorIndex; import zeldaswordskills.client.ZSSKeyHandler; import zeldaswordskills.entity.player.ZSSPlayerInfo; import zeldaswordskills.entity.player.ZSSPlayerSkills; import zeldaswordskills.entity.projectile.EntitySwordBeam; import zeldaswordskills.item.ZSSItems; import zeldaswordskills.network.PacketDispatcher; import zeldaswordskills.network.bidirectional.ActivateSkillPacket; import zeldaswordskills.ref.Config; import zeldaswordskills.ref.Sounds; import zeldaswordskills.skills.ICombo; import zeldaswordskills.skills.SkillActive; import zeldaswordskills.util.PlayerUtils; import zeldaswordskills.util.WorldUtils; /** * * SWORD BEAM * Description: Shoot a beam of energy from the sword tip * Activation: Attack while sneaking and at near full health * Effect: Shoots a ranged beam capable of damaging one or possibly more targets * Damage: 30 + (level * 10) percent of the base sword damage (without other bonuses) * Range: Approximately 12 blocks, plus one block per level * Magic: 10.0F regardless of level * Special: * - May only be used while locked on to a target * - Amount of health required decreases with skill level, down to 1-1/2 hearts below max * - Hitting a target with the beam counts as a direct strike for combos * - Using the Master Sword will shoot a beam that can penetrate multiple targets * - Each additional target receives 20% less damage than the previous * - Cannot unleash a second beam until the previous beam expires or strikes a target * * Sword beam shot from Link's sword when at full health. Inflicts the sword's full * base damage, not including enchantment or other bonuses, to the first entity struck. * * If using the Master Sword, the beam will shoot through enemies, hitting all targets * in its direct path. * */ public class SwordBeam extends SkillActive { /** Used to end combo if the sword beam fails to strike a target */ private int missTimer; public SwordBeam(String name) { super(name); } private SwordBeam(SwordBeam skill) { super(skill); } @Override public SwordBeam newInstance() { return new SwordBeam(this); } @Override @SideOnly(Side.CLIENT) public void addInformation(List<String> desc, EntityPlayer player) { desc.add(getDamageDisplay(getDamageFactor(player), false) + "%"); desc.add(getRangeDisplay(12 + level)); desc.add(StatCollector.translateToLocalFormatted(getInfoString("info", 1), String.format("%.1f", Config.getHealthAllowance(level) / 2.0F))); desc.add(StatCollector.translateToLocalFormatted(getInfoString("info", 2), String.format("%.2f", getMagicCost()))); } @Override public boolean isActive() { return missTimer > 0; } @Override public boolean hasAnimation() { return false; } @Override protected float getExhaustion() { return 0.0F; } private float getMagicCost() { return 10.0F; } /** Returns true if players current health is within the allowed limit */ private boolean checkHealth(EntityPlayer player) { return PlayerUtils.getHealthMissing(player) <= Config.getHealthAllowance(level); } /** The percent of base sword damage that should be inflicted, as an integer */ private int getDamageFactor(EntityPlayer player) { ItemStack mask = player.getEquipmentInSlot(ArmorIndex.EQUIPPED_HELM); int base = (mask != null && mask.getItem() == ZSSItems.maskFierce) ? 55 : 30; return base + (level * 10); } /** Returns player's base damage (with sword) plus 1.0F per level */ private float getDamage(EntityPlayer player) { return (float)((double)(getDamageFactor(player)) * 0.01D * player.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue()); } @Override public boolean canUse(EntityPlayer player) { ZSSPlayerInfo info = ZSSPlayerInfo.get(player); if (super.canUse(player) && !isActive() && info.canUseMagic() && PlayerUtils.isSword(player.getHeldItem())) { return (player.capabilities.isCreativeMode || (info.canAttack() && info.getCurrentMagic() >= getMagicCost() && checkHealth(player))); } return false; } /** * Player must be on ground to prevent conflict with RisingCut */ @Override @SideOnly(Side.CLIENT) public boolean canExecute(EntityPlayer player) { return player.onGround && player.isSneaking() && canUse(player); } @Override @SideOnly(Side.CLIENT) public boolean isKeyListener(Minecraft mc, KeyBinding key) { return (key == ZSSKeyHandler.keys[ZSSKeyHandler.KEY_ATTACK] || (Config.allowVanillaControls && key == mc.gameSettings.keyBindAttack)); } @Override @SideOnly(Side.CLIENT) public boolean keyPressed(Minecraft mc, KeyBinding key, EntityPlayer player) { if (canExecute(player)) { PacketDispatcher.sendToServer(new ActivateSkillPacket(this)); return true; } return false; } @Override protected boolean onActivated(World world, EntityPlayer player) { player.swingItem(); if (!world.isRemote) { if (!ZSSPlayerInfo.get(player).useMagic(getMagicCost())) { return false; } missTimer = 12 + level; WorldUtils.playSoundAtEntity(player, Sounds.WHOOSH, 0.4F, 0.5F); Vec3 vec3 = player.getLookVec(); EntitySwordBeam beam = new EntitySwordBeam(world, player).setLevel(level); beam.setDamage(getDamage(player)); beam.setMasterSword(PlayerUtils.isHoldingMasterSword(player)); beam.setPosition(beam.posX + vec3.xCoord * 2, beam.posY + vec3.yCoord * 2, beam.posZ + vec3.zCoord * 2); world.spawnEntityInWorld(beam); } return true; } @Override protected void onDeactivated(World world, EntityPlayer player) { missTimer = 0; } @Override public void onUpdate(EntityPlayer player) { if (missTimer > 0) { --missTimer; if (missTimer == 0 && !player.worldObj.isRemote) { ICombo combo = ZSSPlayerSkills.get(player).getComboSkill(); if (combo != null && combo.isComboInProgress()) { combo.getCombo().endCombo(player); } } } } /** * Call from {@link EntitySwordBeam#onImpact} to allow handling of ICombo; * striking an entity sets the missTimer to zero * @param hitBlock true if sword beam hit a block rather than an entity */ public void onImpact(EntityPlayer player, boolean hitBlock) { missTimer = (hitBlock && missTimer > 0 ? 1 : 0); } }