/* OrpheusMS: MapleStory Private Server based on OdinMS Copyright (C) 2012 Aaron Weiss <aaron@deviant-core.net> Patrick Huy <patrick.huy@frz.cc> Matthias Butz <matze@odinms.de> Jan Christian Meyer <vimes@odinms.de> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.server.handlers.channel; import client.IItem; import client.ISkill; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleCharacter.CancelCooldownAction; import client.MapleClient; import client.MapleInventory; import client.MapleInventoryType; import client.MapleWeaponType; import client.SkillFactory; import constants.ItemConstants; import constants.ServerConstants; import constants.skills.Aran; import constants.skills.Buccaneer; import constants.skills.NightLord; import constants.skills.NightWalker; import constants.skills.Shadower; import constants.skills.ThunderBreaker; import constants.skills.WindArcher; import tools.Randomizer; import net.MaplePacket; import server.MapleInventoryManipulator; import server.MapleItemInformationProvider; import server.MapleStatEffect; import server.TimerManager; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; public final class RangedAttackHandler extends AbstractDealDamageHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter player = c.getPlayer(); AttackInfo attack = parseDamage(slea, player, true); if (attack.skill == Buccaneer.ENERGY_ORB || attack.skill == ThunderBreaker.SPARK || attack.skill == Shadower.TAUNT || attack.skill == NightLord.TAUNT) { player.getMap().broadcastMessage(player, MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); applyAttack(attack, player, 1); } else if (attack.skill == Aran.COMBO_SMASH || attack.skill == Aran.COMBO_PENRIL || attack.skill == Aran.COMBO_TEMPEST) { player.getMap().broadcastMessage(player, MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false); if (attack.skill == Aran.COMBO_SMASH && player.getCombo() >= 30) { applyAttack(attack, player, 1); } else if (attack.skill == Aran.COMBO_PENRIL && player.getCombo() >= 100) { applyAttack(attack, player, 2); } else if (attack.skill == Aran.COMBO_TEMPEST && player.getCombo() >= 200) { applyAttack(attack, player, 4); } } else { IItem weapon = player.getInventory(MapleInventoryType.EQUIPPED).getItem((byte) -11); MapleWeaponType type = MapleItemInformationProvider.getInstance().getWeaponType(weapon.getItemId()); if (type == MapleWeaponType.NOT_A_WEAPON) { return; } int projectile = 0; byte bulletCount = 1; MapleStatEffect effect = null; if (attack.skill != 0) { effect = attack.getAttackEffect(player, null); bulletCount = effect.getBulletCount(); if (effect.getCooldown() > 0) { c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect.getCooldown())); } } boolean hasShadowPartner = player.getBuffedValue(MapleBuffStat.SHADOWPARTNER) != null; if (hasShadowPartner) { bulletCount *= 2; } MapleInventory inv = player.getInventory(MapleInventoryType.USE); for (byte i = 0; i < inv.getSlotLimit(); i++) { IItem item = inv.getItem(i); if (item != null) { int id = item.getItemId(); boolean bow = ItemConstants.isArrowForBow(id); boolean cbow = ItemConstants.isArrowForCrossBow(id); if (item.getQuantity() > (bulletCount == 1 ? 0 : bulletCount)) { if (type == MapleWeaponType.CLAW && ItemConstants.isThrowingStar(id) && weapon.getItemId() != 1472063) { if (((id == 2070007 || id == 2070018) && player.getLevel() < 70) || (id == 2070016 && player.getLevel() < 50)) { } else { projectile = id; break; } } else if ((type == MapleWeaponType.GUN && ItemConstants.isBullet(id))) { if (id == 2331000 && id == 2332000) { if (player.getLevel() > 69) { projectile = id; break; } } else if (player.getLevel() > (id % 10) * 20 + 9) { projectile = id; break; } } else if ((type == MapleWeaponType.BOW && bow) || (type == MapleWeaponType.CROSSBOW && cbow) || (weapon.getItemId() == 1472063 && (bow || cbow))) { projectile = id; break; } } } } boolean soulArrow = player.getBuffedValue(MapleBuffStat.SOULARROW) != null; boolean shadowClaw = player.getBuffedValue(MapleBuffStat.SHADOW_CLAW) != null; if (!soulArrow && !shadowClaw && attack.skill != 11101004 && attack.skill != 15111007 && attack.skill != 14101006) { byte bulletConsume = bulletCount; if (effect != null && effect.getBulletConsume() != 0) { bulletConsume = (byte) (effect.getBulletConsume() * (hasShadowPartner ? 2 : 1)); } if (!ServerConstants.UNLIMITED_PROJECTILES) { MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, projectile, bulletConsume, false, true); } } if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) { int visProjectile = projectile; // visible projectile sent to // players if (ItemConstants.isThrowingStar(projectile)) { MapleInventory cash = player.getInventory(MapleInventoryType.CASH); for (int i = 0; i < 96; i++) { // impose order... IItem item = cash.getItem((byte) i); if (item != null) { if (item.getItemId() / 1000 == 5021) { visProjectile = item.getItemId(); break; } } } } else // bow, crossbow if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) { visProjectile = 0; } MaplePacket packet; switch (attack.skill) { case 3121004: // Hurricane case 3221001: // Pierce case 5221004: // Rapid Fire case 13111002: // KoC Hurricane packet = MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.rangedirection, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display); break; default: packet = MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display); break; } player.getMap().broadcastMessage(player, packet, false, true); if (effect != null) { int money = effect.getMoneyCon(); if (money != 0) { int moneyMod = money / 2; money += Randomizer.nextInt(moneyMod); if (money > player.getMeso()) { money = player.getMeso(); } player.gainMeso(-money, false); } } if (attack.skill != 0) { ISkill skill = SkillFactory.getSkill(attack.skill); MapleStatEffect effect_ = skill.getEffect(player.getSkillLevel(skill)); if (effect_.getCooldown() > 0) { if (player.skillisCooling(attack.skill)) { return; } else { c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect_.getCooldown())); player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000, TimerManager.getInstance().schedule(new CancelCooldownAction(player, attack.skill), effect_.getCooldown() * 1000)); } } } if ((player.getSkillLevel(SkillFactory.getSkill(NightWalker.VANISH)) > 0 || player.getSkillLevel(SkillFactory.getSkill(WindArcher.WIND_WALK)) > 0) && player.getBuffedValue(MapleBuffStat.DARKSIGHT) != null && attack.numAttacked > 0 && player.getBuffSource(MapleBuffStat.DARKSIGHT) != 9101004) { player.cancelEffectFromBuffStat(MapleBuffStat.DARKSIGHT); player.cancelBuffStats(MapleBuffStat.DARKSIGHT); } applyAttack(attack, player, bulletCount); } } } }