/**
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.Entity;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.DamageSource;
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.client.ZSSKeyHandler;
import zeldaswordskills.entity.ZSSEntityInfo;
import zeldaswordskills.entity.buff.Buff;
import zeldaswordskills.entity.player.ZSSPlayerSkills;
import zeldaswordskills.network.PacketDispatcher;
import zeldaswordskills.network.bidirectional.ActivateSkillPacket;
import zeldaswordskills.ref.Config;
import zeldaswordskills.ref.Sounds;
import zeldaswordskills.skills.SkillActive;
import zeldaswordskills.util.PlayerUtils;
/**
*
* DODGE
* Description: Avoid damage by quickly dodging out of the way
* Activation: Tap left or right arrow key to dodge in that direction
* Exhaustion: 0.05F
* Duration: (5 + level) ticks; this is the amount of time during which damage may be avoided
* Special: - May only be used while locked on to a target
* - Chance to avoid damage is 10% per level, plus a timing bonus of up to 20%
*
* Tap 'left' or 'right' arrow key to dodge out of harm's way (activated when key released);
* can be configured to require double-tap and / or to allow use of default movement keys ('a'
* and 'd') for activation in addition to arrow keys.
*
* While dodging, there is a chance to avoid any incoming attacks.
*
*/
public class Dodge extends SkillActive
{
/** Key that was pressed to initiate dodge */
@SideOnly(Side.CLIENT)
private KeyBinding keyPressed;
/** Current number of ticks remaining before dodge will not activate */
@SideOnly(Side.CLIENT)
private int ticksTilFail;
/** Timer during which player may evade incoming attacks */
private int dodgeTimer = 0;
/** Entity dodged, since the attack event may fire multiple times in quick succession for mobs like zombies */
// TODO make a List<Entity>, for dodging multiple entities; lower dodge chance as list grows in size
// TODO perhaps limit the size of the list by skill level
private Entity entityDodged;
public Dodge(String name) {
super(name);
}
private Dodge(Dodge skill) {
super(skill);
}
@Override
public Dodge newInstance() {
return new Dodge(this);
}
@Override
@SideOnly(Side.CLIENT)
public void addInformation(List<String> desc, EntityPlayer player) {
desc.add(StatCollector.translateToLocalFormatted(getInfoString("info", 1),
(int)(getBaseDodgeChance(player) * 100)));
desc.add(StatCollector.translateToLocalFormatted(getInfoString("info", 2),
(getDodgeTime() + level - 5) * 2)); // don't use real time bonus, since timer is zero
desc.add(getTimeLimitDisplay(getDodgeTime()));
desc.add(getExhaustionDisplay(getExhaustion()));
}
/**
* Prevents Dodge from being activated in quick succession, but does not prevent
* other skills from being activated once Dodge has finished animating
*/
@Override
public boolean isActive() {
return (dodgeTimer > 0);
}
@Override
protected float getExhaustion() {
return 0.05F;
}
/** Returns player's base chance to successfully evade an attack, including bonuses from buffs */
private float getBaseDodgeChance(EntityPlayer player) {
float evadeUp = ZSSEntityInfo.get(player).getBuffAmplifier(Buff.EVADE_UP) * 0.01F;
float evadeDown = ZSSEntityInfo.get(player).getBuffAmplifier(Buff.EVADE_DOWN) * 0.01F;
float speedBonus = 2.0F * (float)(player.getAttributeMap().getAttributeInstance(SharedMonsterAttributes.movementSpeed).getAttributeValue() - Dash.BASE_MOVE);
return ((level * 0.1F) + (evadeUp - evadeDown) + speedBonus);
}
/** Returns full chance to dodge an attack, including all bonuses */
private float getDodgeChance(EntityPlayer player) {
return getBaseDodgeChance(player) + getTimeBonus();
}
/** Amount of time dodge will remain active */
private int getDodgeTime() {
return (5 + level);
}
/** Returns timing evasion bonus */
private float getTimeBonus() {
return ((dodgeTimer + level - 5) * 0.02F);
}
@Override
public boolean canUse(EntityPlayer player) {
return super.canUse(player) && !isActive() && ZSSPlayerSkills.get(player).isSkillActive(swordBasic);
}
@Override
@SideOnly(Side.CLIENT)
public boolean canExecute(EntityPlayer player) {
return player.onGround && canUse(player);
}
@Override
@SideOnly(Side.CLIENT)
public boolean isKeyListener(Minecraft mc, KeyBinding key) {
return ((Config.allowVanillaControls && (key == mc.gameSettings.keyBindLeft || key == mc.gameSettings.keyBindRight)) ||
key == ZSSKeyHandler.keys[ZSSKeyHandler.KEY_LEFT] || key == ZSSKeyHandler.keys[ZSSKeyHandler.KEY_RIGHT]);
}
@Override
@SideOnly(Side.CLIENT)
public boolean keyPressed(Minecraft mc, KeyBinding key, EntityPlayer player) {
if (canExecute(player)) {
if (Config.requireDoubleTap) {
if (ticksTilFail > 0 && key == keyPressed) {
PacketDispatcher.sendToServer(new ActivateSkillPacket(this));
ticksTilFail = 0;
return true;
} else {
keyPressed = key;
ticksTilFail = 6;
}
// Single-tap activation only allowed using custom key bindings:
} else if (key == ZSSKeyHandler.keys[ZSSKeyHandler.KEY_LEFT] || key == ZSSKeyHandler.keys[ZSSKeyHandler.KEY_RIGHT]) {
PacketDispatcher.sendToServer(new ActivateSkillPacket(this));
return true;
}
}
return false; // allow other skills to receive this key press (e.g. Spin Attack)
}
@Override
public boolean onActivated(World world, EntityPlayer player) {
dodgeTimer = getDodgeTime();
entityDodged = null;
return isActive();
}
@Override
protected void onDeactivated(World world, EntityPlayer player) {
dodgeTimer = 0;
entityDodged = null;
}
@Override
public void onUpdate(EntityPlayer player) {
if (isActive()) {
--dodgeTimer;
} else if (player.worldObj.isRemote && ticksTilFail > 0) {
--ticksTilFail;
}
}
@Override
@SideOnly(Side.CLIENT)
public boolean isAnimating() {
return (dodgeTimer > level);
}
@Override
@SideOnly(Side.CLIENT)
public boolean onRenderTick(EntityPlayer player, float partialTickTime) {
double speed = 1.0D + 10.0D * (player.getAttributeMap().getAttributeInstance(SharedMonsterAttributes.movementSpeed).getAttributeValue() - Dash.BASE_MOVE);
if (speed > 1.0D) {
speed = 1.0D;
}
double d = 0.15D * speed * speed;
Vec3 vec3 = player.getLookVec();
if (keyPressed == ZSSKeyHandler.keys[ZSSKeyHandler.KEY_RIGHT] || keyPressed == Minecraft.getMinecraft().gameSettings.keyBindRight) {
player.addVelocity(-vec3.zCoord * d, 0.0D, vec3.xCoord * d);
} else {
player.addVelocity(vec3.zCoord * d, 0.0D, -vec3.xCoord * d);
}
return true;
}
@Override
public boolean onBeingAttacked(EntityPlayer player, DamageSource source) {
if (dodgeTimer > level) { // still able to dodge (used to use isActive(), but changed for animating)
Entity attacker = source.getEntity();
if (attacker != null) {
return (attacker == entityDodged || dodgeAttack(player, attacker));
}
}
return false;
}
/**
* Returns true if the attack was dodged and the attack event should be canceled
*/
private boolean dodgeAttack(EntityPlayer player, Entity attacker) {
if (player.worldObj.rand.nextFloat() < getDodgeChance(player)) {
entityDodged = attacker;
PlayerUtils.playRandomizedSound(player, Sounds.SWORD_MISS, 0.4F, 0.5F);
return true;
}
return false;
}
}