package com.asteria.net.message.impl; import com.asteria.game.World; import com.asteria.game.character.combat.magic.CombatSpell; import com.asteria.game.character.combat.magic.CombatSpells; import com.asteria.game.character.npc.Npc; import com.asteria.game.character.npc.NpcDefinition; import com.asteria.game.character.player.Player; import com.asteria.game.character.player.minigame.MinigameHandler; import com.asteria.game.location.Location; import com.asteria.game.location.Position; import com.asteria.game.plugin.context.NpcFirstClickPlugin; import com.asteria.game.plugin.context.NpcSecondClickPlugin; import com.asteria.net.ByteOrder; import com.asteria.net.ValueType; import com.asteria.net.message.InputMessageListener; import com.asteria.net.message.MessageBuilder; /** * The message sent from the client when a player attacks or clicks on an NPC. * * @author lare96 <http://github.com/lare96> */ public final class NpcActionMessage implements InputMessageListener { @Override public void handleMessage(Player player, int opcode, int size, MessageBuilder payload) { if (player.isDisabled()) return; switch (opcode) { case 72: attackOther(player, payload); break; case 131: attackMagic(player, payload); break; case 155: firstClick(player, payload); break; case 17: secondClick(player, payload); break; } } /** * Handles the melee and ranged attacks on an NPC. * * @param player * the player this will be handled for. * @param payload * the payloadfer that will read the sent data. */ private void attackOther(Player player, MessageBuilder payload) { int index = payload.getShort(false, ValueType.A); Npc npc = World.getNpcs().get(index); if (npc == null || !checkAttack(player, npc)) return; player.getTolerance().reset(); player.getCombatBuilder().attack(npc); } /** * Handles the magic attacks on an NPC. * * @param player * the player this will be handled for. * @param payload * the payloadfer that will read the sent data. */ private void attackMagic(Player player, MessageBuilder payload) { int index = payload.getShort(true, ValueType.A, ByteOrder.LITTLE); int spellId = payload.getShort(true, ValueType.A); Npc npc = World.getNpcs().get(index); CombatSpell spell = CombatSpells.getSpell(spellId).orElse(null).getSpell(); if (npc == null || spell == null || !checkAttack(player, npc)) return; player.setCastSpell(spell); player.getTolerance().reset(); player.getCombatBuilder().attack(npc); } /** * Handles the first click NPC slot. * * @param player * the player this will be handled for. * @param payload * the payloadfer that will read the sent data. */ private void firstClick(Player player, MessageBuilder payload) { int index = payload.getShort(true, ByteOrder.LITTLE); Npc npc = World.getNpcs().get(index); if (npc == null) return; Position position = npc.getPosition().copy(); player.getMovementListener().append(() -> { if (player.getPosition().withinDistance(position, 1)) { player.facePosition(npc.getPosition()); npc.facePosition(player.getPosition()); MinigameHandler.execute(player, m -> m.onFirstClickNpc(player, npc)); World.getPlugins().execute(player, NpcFirstClickPlugin.class, new NpcFirstClickPlugin(npc)); } }); } /** * Handles the second click NPC slot. * * @param player * the player this will be handled for. * @param payload * the payloadfer that will read the sent data. */ private void secondClick(Player player, MessageBuilder payload) { int index = payload.getShort(false, ValueType.A, ByteOrder.LITTLE); Npc npc = World.getNpcs().get(index); if (npc == null) return; Position position = npc.getPosition().copy(); player.getMovementListener().append(() -> { if (player.getPosition().withinDistance(position, 1)) { player.facePosition(npc.getPosition()); npc.facePosition(player.getPosition()); MinigameHandler.execute(player, m -> m.onSecondClickNpc(player, npc)); World.getPlugins().execute(player, NpcSecondClickPlugin.class, new NpcSecondClickPlugin(npc)); } }); } /** * Determines if {@code player} can make an attack on {@code npc}. * * @param player * the player attempting to make an attack. * @param npc * the npc being attacked. * @return {@code true} if the player can make an attack, {@code false} * otherwise. */ private boolean checkAttack(Player player, Npc npc) { if (!NpcDefinition.DEFINITIONS[npc.getId()].isAttackable()) return false; if (!MinigameHandler.execute(player, true, m -> m.canHit(player, npc))) return false; if (!Location.inMultiCombat(player) && player.getCombatBuilder().isBeingAttacked() && !npc.equals(player.getCombatBuilder() .getLastAttacker())) { player.getMessages().sendMessage("You are already under attack!"); return false; } return true; } }