/** 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.client; import net.minecraft.client.Minecraft; import net.minecraft.client.settings.KeyBinding; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.BlockPos; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumFacing; import net.minecraft.util.MathHelper; import net.minecraft.util.StatCollector; import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.InputEvent.KeyInputEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.lwjgl.input.Keyboard; import zeldaswordskills.api.item.ISwingSpeed; import zeldaswordskills.entity.ZSSEntityInfo; import zeldaswordskills.entity.buff.Buff; import zeldaswordskills.entity.player.ZSSPlayerInfo; import zeldaswordskills.entity.player.ZSSPlayerSkills; import zeldaswordskills.handler.GuiHandler; import zeldaswordskills.item.ICyclableItem; import zeldaswordskills.item.ItemHeldBlock; import zeldaswordskills.network.PacketDispatcher; import zeldaswordskills.network.bidirectional.ActivateSkillPacket; import zeldaswordskills.network.server.CycleItemModePacket; import zeldaswordskills.network.server.GetBombPacket; import zeldaswordskills.network.server.OpenGuiPacket; import zeldaswordskills.ref.Config; import zeldaswordskills.skills.ILockOnTarget; import zeldaswordskills.skills.SkillBase; import zeldaswordskills.util.PlayerUtils; @SideOnly(Side.CLIENT) public class ZSSKeyHandler { private final Minecraft mc; /** Key index for easy handling and retrieval of keys and key descriptions */ public static final byte KEY_SKILL_ACTIVATE = 0, KEY_NEXT_TARGET = 1, KEY_ATTACK = 2, KEY_LEFT = 3, KEY_RIGHT = 4, KEY_DOWN = 5, KEY_BLOCK = 6, KEY_BOMB = 7, KEY_TOGGLE_AUTOTARGET = 8, KEY_TOGGLE_BUFFBAR = 9, KEY_SKILLS_GUI = 10, KEY_PREV_MODE = 11, KEY_NEXT_MODE = 12; /** Key descriptions - this is what the player sees when changing key bindings in-game */ public static final String[] desc = { "activate","next","attack","left","right","down", "block","bomb","toggleat","togglebuff", "skills_gui", "prev_mode", "next_mode"}; /** Default key values */ private static final int[] keyValues = {Keyboard.KEY_X, Keyboard.KEY_TAB, Keyboard.KEY_UP, Keyboard.KEY_LEFT, Keyboard.KEY_RIGHT, Keyboard.KEY_DOWN, Keyboard.KEY_RCONTROL, Keyboard.KEY_B, Keyboard.KEY_PERIOD, Keyboard.KEY_V, Keyboard.KEY_P, Keyboard.KEY_LBRACKET, Keyboard.KEY_RBRACKET}; public static final KeyBinding[] keys = new KeyBinding[desc.length]; public ZSSKeyHandler() { this.mc = Minecraft.getMinecraft(); for (int i = 0; i < desc.length; ++i) { keys[i] = new KeyBinding("key.zss." + desc[i] + ".desc", keyValues[i], StatCollector.translateToLocal("key.label")); ClientRegistry.registerKeyBinding(keys[i]); } } @SubscribeEvent public void onKeyInput(KeyInputEvent event) { if (Keyboard.getEventKeyState()) { onKeyPressed(mc, Keyboard.getEventKey()); } } /** * Call for any key code, mouse or keyboard, to handle custom key bindings that may * have been remapped to mouse. From MouseEvent, ONLY call this method when the mouse * key is pressed, not when it is released (i.e. when event.buttonstate is true). * @param mc Pass in Minecraft instance, since this is a static method * @param kb The key code of the key pressed; for the mouse, this is the mouse button number minus 100 */ public static void onKeyPressed(Minecraft mc, int kb) { if (mc.inGameHasFocus && mc.thePlayer != null) { ZSSPlayerSkills skills = ZSSPlayerSkills.get(mc.thePlayer); if (kb == mc.gameSettings.keyBindSprint.getKeyCode()) { // Don't allow sprinting while in mid-air (motionY is < 0 even when standing on a block) int x = MathHelper.floor_double(mc.thePlayer.posX); int y = MathHelper.floor_double(mc.thePlayer.posY - mc.thePlayer.getYOffset()); int z = MathHelper.floor_double(mc.thePlayer.posZ); if (!mc.theWorld.isSideSolid(new BlockPos(x, y - 1, z), EnumFacing.UP)) { KeyBinding.setKeyBindState(kb, false); } } else if (kb == keys[KEY_SKILL_ACTIVATE].getKeyCode()) { if (skills.hasSkill(SkillBase.swordBasic)) { PacketDispatcher.sendToServer(new ActivateSkillPacket(SkillBase.swordBasic)); } } else if (kb == keys[KEY_BOMB].getKeyCode()) { // prevent player from holding RMB while getting bombs, as it can crash the game if (mc.gameSettings.keyBindUseItem.isKeyDown()) { KeyBinding.setKeyBindState(mc.gameSettings.keyBindUseItem.getKeyCode(), false); } PacketDispatcher.sendToServer(new GetBombPacket()); } else if (kb == keys[KEY_TOGGLE_AUTOTARGET].getKeyCode()) { if (mc.thePlayer.isSneaking()) { PlayerUtils.sendTranslatedChat(mc.thePlayer, "chat.zss.key.toggletp", (Config.toggleTargetPlayers() ? new ChatComponentTranslation("chat.zss.key.enable") : new ChatComponentTranslation("chat.zss.key.disable"))); } else { PlayerUtils.sendTranslatedChat(mc.thePlayer, "chat.zss.key.toggleat", (Config.toggleAutoTarget() ? new ChatComponentTranslation("chat.zss.key.enable") : new ChatComponentTranslation("chat.zss.key.disable"))); } } else if (kb == keys[KEY_TOGGLE_BUFFBAR].getKeyCode()) { if (mc.thePlayer.isSneaking()) { Config.isComboHudEnabled = !Config.isComboHudEnabled; PlayerUtils.sendTranslatedChat(mc.thePlayer, "chat.zss.key.togglehud", (Config.isComboHudEnabled ? new ChatComponentTranslation("chat.zss.key.enable") : new ChatComponentTranslation("chat.zss.key.disable"))); } else { Config.isBuffBarEnabled = !Config.isBuffBarEnabled; } } else if (kb == keys[KEY_SKILLS_GUI].getKeyCode()) { PacketDispatcher.sendToServer(new OpenGuiPacket(GuiHandler.GUI_SKILLS)); } else if (kb == keys[KEY_PREV_MODE].getKeyCode() || kb == keys[KEY_NEXT_MODE].getKeyCode()) { boolean next = kb == keys[KEY_NEXT_MODE].getKeyCode(); ItemStack stack = mc.thePlayer.getHeldItem(); if (stack != null && stack.getItem() instanceof ICyclableItem) { if (next) { ((ICyclableItem) stack.getItem()).nextItemMode(stack, mc.thePlayer); } else { ((ICyclableItem) stack.getItem()).prevItemMode(stack, mc.thePlayer); } PacketDispatcher.sendToServer(new CycleItemModePacket(next)); } } else { handleTargetingKeys(mc, kb, skills); } } } /** * All ILockOnTarget skill related keys are handled here */ private static void handleTargetingKeys(Minecraft mc, int kb, ZSSPlayerSkills skills) { ILockOnTarget skill = skills.getTargetingSkill(); // key interaction is disabled if the player is stunned or a skill animation is in progress boolean canInteract = skills.canInteract() && !ZSSEntityInfo.get(mc.thePlayer).isBuffActive(Buff.STUN); if (skill == null || !skill.isLockedOn()) { return; } if (kb == keys[KEY_NEXT_TARGET].getKeyCode()) { skill.getNextTarget(mc.thePlayer); } else if (kb == keys[KEY_ATTACK].getKeyCode() || kb == mc.gameSettings.keyBindAttack.getKeyCode()) { KeyBinding key = (kb == keys[KEY_ATTACK].getKeyCode() ? keys[KEY_ATTACK] : mc.gameSettings.keyBindAttack); Item heldItem = (mc.thePlayer.getHeldItem() != null ? mc.thePlayer.getHeldItem().getItem() : null); boolean flag = (heldItem instanceof ItemHeldBlock || (!ZSSPlayerInfo.get(mc.thePlayer).canAttack() && (Config.affectAllSwings() || heldItem instanceof ISwingSpeed))); if (canInteract && !flag) { KeyBinding.setKeyBindState(key.getKeyCode(), true); } else if (!flag) { // hack for Super Spin Attack, as it requires key press to be passed while animation is in progress if (skills.isSkillActive(SkillBase.spinAttack)) { skills.getActiveSkill(SkillBase.spinAttack).keyPressed(mc, key, mc.thePlayer); return; } else if (skills.isSkillActive(SkillBase.backSlice)) { skills.getActiveSkill(SkillBase.backSlice).keyPressed(mc, key, mc.thePlayer); return; } } // Only allow attack key to continue processing if it was set to pressed if (key.isKeyDown()) { if (!skills.onKeyPressed(mc, key)) { ZSSClientEvents.performComboAttack(mc, skill); } // hack for Armor Break to begin charging without having to press attack again if (skills.hasSkill(SkillBase.armorBreak)) { skills.getActiveSkill(SkillBase.armorBreak).keyPressed(mc, key, mc.thePlayer); } } } else if (canInteract) { // Only works for keys mapped to custom key bindings, which is fine for remapped mouse keys KeyBinding key = getKeyBindFromCode(mc, kb); if (key != null) { KeyBinding.setKeyBindState(kb, true); skills.onKeyPressed(mc, key); } } } /** * Returns the KeyBinding corresponding to the key code given, or NULL if no key binding is found * Currently handles all custom keys, plus the following vanilla keys: * Always allowed: keyBindForward, keyBindJump * {@link Config#allowVanillaControls}: keyBindLeft, keyBindRight, keyBindBack * @param keyCode Will be a negative number for mouse keys, or positive for keyboard * @param mc Pass in Minecraft instance as a workaround to get vanilla KeyBindings */ @SideOnly(Side.CLIENT) public static KeyBinding getKeyBindFromCode(Minecraft mc, int keyCode) { for (KeyBinding k : keys) { if (k.getKeyCode() == keyCode) { return k; } } // there must be a better way to do this... if (keyCode == mc.gameSettings.keyBindForward.getKeyCode()) { return mc.gameSettings.keyBindForward; } else if (keyCode == mc.gameSettings.keyBindJump.getKeyCode()) { return mc.gameSettings.keyBindJump; } else if (Config.allowVanillaControls) { if (keyCode == mc.gameSettings.keyBindLeft.getKeyCode()) { return mc.gameSettings.keyBindLeft; } else if (keyCode == mc.gameSettings.keyBindRight.getKeyCode()) { return mc.gameSettings.keyBindRight; } else if (keyCode == mc.gameSettings.keyBindBack.getKeyCode()) { return mc.gameSettings.keyBindBack; } } // Doesn't seem to be an easy way to get the KeyBinding from the key code... /* curse cpw for making both the key bind array and hash lookup private >.< Iterator iterator = KeyBinding.keybindArray.iterator(); while (iterator.hasNext()) { KeyBinding kb = (KeyBinding) iterator.next(); if (kb.getKeyCode() == keyCode) { return kb; } } */ return null; } }