package javastory.channel.handling; import java.awt.Point; import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.logging.Level; import java.util.logging.Logger; import javastory.channel.ChannelCharacter; import javastory.channel.ChannelClient; import javastory.channel.ChannelServer; import javastory.channel.anticheat.CheatingOffense; import javastory.channel.client.ActivePlayerStats; import javastory.channel.client.BuffStat; import javastory.channel.client.CancelCooldownAction; import javastory.channel.client.ISkill; import javastory.channel.client.KeyBinding; import javastory.channel.client.SkillMacro; import javastory.channel.life.MobSkill; import javastory.channel.life.Monster; import javastory.channel.maps.FieldLimitType; import javastory.channel.maps.GameMap; import javastory.channel.maps.GameMapFactory; import javastory.channel.maps.ItemDropType; import javastory.channel.movement.AbsoluteLifeMovement; import javastory.channel.movement.LifeMovementFragment; import javastory.channel.packet.MTSCSPacket; import javastory.channel.packet.MobPacket; import javastory.channel.server.AutobanManager; import javastory.channel.server.InventoryManipulator; import javastory.channel.server.Portal; import javastory.channel.server.StatEffect; import javastory.game.AttackType; import javastory.game.GameConstants; import javastory.game.Inventory; import javastory.game.Item; import javastory.game.Skills; import javastory.game.data.ItemInfoProvider; import javastory.game.data.MobAttackInfo; import javastory.game.data.MobAttackInfoFactory; import javastory.game.data.MobSkillFactory; import javastory.game.data.SkillInfoProvider; import javastory.io.PacketFormatException; import javastory.io.PacketReader; import javastory.server.TimerManager; import javastory.tools.packets.ChannelPackets; import javastory.tools.packets.UIPacket; public class PlayerHandler { private static boolean isFinisher(final int skillid) { switch (skillid) { case 1111003: case 1111004: case 1111005: case 1111006: case 11111002: case 11111003: return true; } return false; } public static void handleChangeMonsterBookCover(final int bookid, final ChannelClient c, final ChannelCharacter chr) { if (bookid == 0 || GameConstants.isMonsterCard(bookid)) { chr.setMonsterBookCover(bookid); chr.getMonsterBook().updateCard(c, bookid); } } public static void handleSkillMacro(final PacketReader reader, final ChannelCharacter chr) throws PacketFormatException { final int num = reader.readByte(); String name; int shout, skill1, skill2, skill3; SkillMacro macro; for (int i = 0; i < num; i++) { name = reader.readLengthPrefixedString(); shout = reader.readByte(); skill1 = reader.readInt(); skill2 = reader.readInt(); skill3 = reader.readInt(); macro = new SkillMacro(skill1, skill2, skill3, name, shout, i); chr.getSkillMacros().update(i, macro); } } public static void HandleChangeKeymap(final PacketReader reader, final ChannelCharacter chr) { if (reader.remaining() != 8) { try { // else = pet auto pot reader.skip(4); final int numChanges = reader.readInt(); int key, type, action; KeyBinding newbinding; for (int i = 0; i < numChanges; i++) { key = reader.readInt(); type = reader.readByte(); action = reader.readInt(); newbinding = new KeyBinding(type, action); chr.changeKeybinding(key, newbinding); } } catch (final PacketFormatException ex) { Logger.getLogger(PlayerHandler.class.getName()).log(Level.SEVERE, null, ex); } } } public static void handleUseChair(final int itemId, final ChannelClient c, final ChannelCharacter chr) { final Item toUse = chr.getSetupInventory().findById(itemId); if (toUse == null || toUse.getItemId() != itemId) { chr.getCheatTracker().registerOffense(CheatingOffense.USING_UNAVAILABLE_ITEM, Integer.toString(itemId)); return; } if (itemId == 3011000) { for (final Item item : c.getPlayer().getCashInventory()) { if (item.getItemId() == 5340000) { chr.startFishingTask(false); break; } else if (item.getItemId() == 5340001) { chr.startFishingTask(true); break; } } } chr.setChair(itemId); chr.getMap().broadcastMessage(chr, ChannelPackets.showChair(chr.getId(), itemId), false); c.write(ChannelPackets.enableActions()); } public static void handleCancelChair(final short id, final ChannelClient c, final ChannelCharacter chr) { if (id == -1) { // Cancel Chair if (chr.getChair() == 3011000) { chr.cancelFishingTask(); } chr.setChair(0); c.write(ChannelPackets.cancelChair(-1)); chr.getMap().broadcastMessage(chr, ChannelPackets.showChair(chr.getId(), 0), false); } else { // Use In-Map Chair chr.setChair(id); c.write(ChannelPackets.cancelChair(id)); } } public static void handleTeleportRockAddMap(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { final byte addrem = reader.readByte(); final byte vip = reader.readByte(); if (addrem == 0) { chr.deleteFromRocks(reader.readInt()); } else if (addrem == 1) { if (chr.getMap().getForcedReturnId() == 999999999) { chr.addRockMap(); } } c.write(MTSCSPacket.getTrockRefresh(chr, vip == 1, addrem == 3)); } public static void handleCharacterInfoRequest(final int objectid, final ChannelClient c, final ChannelCharacter chr) { final ChannelCharacter player = (ChannelCharacter) c.getPlayer().getMap().getMapObject(objectid); if (player != null) { if (!player.isGM() || c.getPlayer().isGM() && player.isGM()) { c.write(ChannelPackets.charInfo(player, c.getPlayer().equals(player))); } else { c.write(ChannelPackets.enableActions()); } } } public static void handleTakeDamage(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { reader.skip(4); // Ticks final byte type = reader.readByte(); reader.skip(1); // Element - 0x00 = elementless, 0x01 = ice, 0x02 = // fire, 0x03 = lightning int damage = reader.readInt(); int oid = 0; int monsteridfrom = 0; final int reflect = 0; byte direction = 0; final int pos_x = 0; final int pos_y = 0; int fake = 0; int mpattack = 0; boolean is_pg = false; boolean isDeadlyAttack = false; Monster attacker = null; final ActivePlayerStats stats = chr.getStats(); if (type != -2 && type != -3 && type != -4) { // Not map damage monsteridfrom = reader.readInt(); oid = reader.readInt(); attacker = chr.getMap().getMonsterByOid(oid); direction = reader.readByte(); if (attacker == null) { return; } if (type != -1) { // Bump damage final MobAttackInfo attackInfo = MobAttackInfoFactory.getInstance().getMobAttackInfo(attacker.getId(), type); if (attackInfo.IsDeadlyAttack) { isDeadlyAttack = true; mpattack = stats.getMp() - 1; } else { mpattack += attackInfo.MpBurn; } final MobSkill skill = MobSkillFactory.getMobSkill(attackInfo.DiseaseSkill, attackInfo.DiseaseLevel); if (skill != null && (damage == -1 || damage > 0)) { skill.applyEffect(chr, attacker, false); } attacker.setMp(attacker.getMp() - attackInfo.MpCon); } } if (damage == -1) { fake = 4020002 + (chr.getJobId() / 10 - 40) * 100000; } else if (damage < -1 || damage > 60000) { AutobanManager.getInstance().addPoints(c, 1000, 60000, "Taking abnormal amounts of damge from " + monsteridfrom + ": " + damage); return; } chr.getCheatTracker().checkTakeDamage(damage); if (damage > 0) { chr.getCheatTracker().setAttacksWithoutHit(false); if (chr.getBuffedValue(BuffStat.MORPH) != null) { chr.cancelMorphs(); } if (type == -1) { if (chr.getBuffedValue(BuffStat.POWERGUARD) != null) { attacker = (Monster) chr.getMap().getMapObject(oid); if (attacker != null) { int bouncedamage = (int) (damage * (chr.getBuffedValue(BuffStat.POWERGUARD).doubleValue() / 100)); bouncedamage = Math.min(bouncedamage, attacker.getMobMaxHp() / 10); attacker.damage(chr, bouncedamage, true); damage -= bouncedamage; chr.getMap().broadcastMessage(chr, MobPacket.damageMonster(oid, bouncedamage), chr.getPosition()); is_pg = true; } } } else if (type != -2 && type != -3 && type != -4) { switch (chr.getJobId()) { case 112: { final ISkill skill = SkillInfoProvider.getSkill(1120004); if (chr.getCurrentSkillLevel(skill) > 0) { damage = (int) (skill.getEffect(chr.getCurrentSkillLevel(skill)).getX() / 1000.0 * damage); } break; } case 122: { final ISkill skill = SkillInfoProvider.getSkill(1220005); if (chr.getCurrentSkillLevel(skill) > 0) { damage = (int) (skill.getEffect(chr.getCurrentSkillLevel(skill)).getX() / 1000.0 * damage); } break; } case 132: { final ISkill skill = SkillInfoProvider.getSkill(1320005); if (chr.getCurrentSkillLevel(skill) > 0) { damage = (int) (skill.getEffect(chr.getCurrentSkillLevel(skill)).getX() / 1000.0 * damage); } break; } } } if (chr.getBuffedValue(BuffStat.MAGIC_GUARD) != null) { int hploss = 0, mploss = 0; if (isDeadlyAttack) { if (stats.getHp() > 1) { hploss = stats.getHp() - 1; } if (stats.getMp() > 1) { mploss = stats.getMp() - 1; } chr.addMPHP(-hploss, -mploss); } else if (mpattack > 0) { chr.addMPHP(-damage, -mpattack); } else { mploss = (int) (damage * (chr.getBuffedValue(BuffStat.MAGIC_GUARD).doubleValue() / 100.0)); hploss = damage - mploss; if (mploss > stats.getMp()) { hploss += mploss - stats.getMp(); mploss = stats.getMp(); } chr.addMPHP(-hploss, -mploss); } } else if (chr.getBuffedValue(BuffStat.MESOGUARD) != null) { damage = damage % 2 == 0 ? damage / 2 : damage / 2 + 1; final int mesoloss = (int) (damage * (chr.getBuffedValue(BuffStat.MESOGUARD).doubleValue() / 100.0)); if (chr.getMeso() < mesoloss) { chr.gainMeso(-chr.getMeso(), false); chr.cancelBuffStats(BuffStat.MESOGUARD); } else { chr.gainMeso(-mesoloss, false); } if (isDeadlyAttack && stats.getMp() > 1) { mpattack = stats.getMp() - 1; } chr.addMPHP(-damage, -mpattack); } else { if (isDeadlyAttack) { chr.addMPHP(stats.getHp() > 1 ? -(stats.getHp() - 1) : 0, stats.getMp() > 1 ? -(stats.getMp() - 1) : 0); } else { chr.addMPHP(-damage, -mpattack); } } } if (!chr.isHidden()) { chr.getMap().broadcastMessage(chr, ChannelPackets.damagePlayer(type, monsteridfrom, chr.getId(), damage, fake, direction, reflect, is_pg, oid, pos_x, pos_y), false); } } public static void handleAranCombo(final ChannelClient c, final ChannelCharacter chr) { if (chr.getJobId() >= 2000 && chr.getJobId() <= 2112) { short combo = chr.getCombo(); final long curr = System.currentTimeMillis(); if (combo > 0 && curr - chr.getLastCombo() > 5000) { // Official MS timing is 2.5 seconds, so 5 seconds should be // safe. chr.getCheatTracker().registerOffense(CheatingOffense.ARAN_COMBO_HACK); } if (combo < 30000) { combo++; } chr.setLastCombo(curr); chr.setCombo(combo); c.write(ChannelPackets.testCombo(combo)); switch (combo) { // Hackish method xD case 10: case 20: case 30: case 40: case 50: case 60: case 70: case 80: case 90: case 100: SkillInfoProvider.getSkill(21000000).getEffect(combo / 10).applyComboBuff(chr, combo); break; } } } public static void handleUseItemEffect(final int itemId, final ChannelClient c, final ChannelCharacter chr) { final Item toUse = chr.getCashInventory().findById(itemId); if (toUse == null || toUse.getItemId() != itemId || toUse.getQuantity() < 1) { c.write(ChannelPackets.enableActions()); return; } chr.setItemEffect(itemId); chr.getMap().broadcastMessage(chr, ChannelPackets.itemEffect(chr.getId(), itemId), false); } public static void handleCancelItemEffect(final int id, final ChannelCharacter chr) { chr.cancelEffect(ItemInfoProvider.getInstance().getItemEffect(-id), false, -1); } public static void handleCancelBuff(final int sourceid, final ChannelCharacter chr) { final ISkill skill = SkillInfoProvider.getSkill(sourceid); if (skill.isChargeSkill()) { chr.setKeyDownSkill_Time(0); chr.getMap().broadcastMessage(chr, ChannelPackets.skillCancel(chr, sourceid), false); } else { chr.cancelEffect(SkillInfoProvider.getSkill(sourceid).getEffect(1), false, -1); } } public static void handleSkillEffect(final PacketReader reader, final ChannelCharacter chr) throws PacketFormatException { final int skillId = reader.readInt(); final byte level = reader.readByte(); final byte flags = reader.readByte(); final byte speed = reader.readByte(); final byte unk = reader.readByte(); // Added on v.82 final ISkill skill = SkillInfoProvider.getSkill(skillId); final int skilllevel_serv = chr.getCurrentSkillLevel(skill); if (skilllevel_serv > 0 && skilllevel_serv == level && skill.isChargeSkill() && level > 0) { chr.setKeyDownSkill_Time(System.currentTimeMillis()); chr.getMap().broadcastMessage(chr, ChannelPackets.skillEffect(chr, skillId, level, flags, speed, unk), false); } } public static void handleSpecialMove(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { if (!chr.isAlive()) { c.write(ChannelPackets.enableActions()); return; } reader.skip(4); // Old X and Y final int skillid = reader.readInt(); final int skillLevel = reader.readByte(); final ISkill skill = SkillInfoProvider.getSkill(skillid); if (chr.getCurrentSkillLevel(skill) == 0 || chr.getCurrentSkillLevel(skill) != skillLevel) { if (!Skills.isMulungSkill(skillid)) { c.disconnect(true); return; } if (chr.getMapId() / 10000 != 92502) { // AutobanManager.getInstance().autoban(c, // "Using Mu Lung dojo skill out of dojo maps."); } else { chr.mulung_EnergyModify(false); } } final StatEffect effect = skill.getEffect(chr.getCurrentSkillLevel(skill)); if (effect.getCooldown() > 0) { if (chr.isInCooldown(skillid)) { c.write(ChannelPackets.enableActions()); return; } if (skillid != 5221006) { // Battleship c.write(ChannelPackets.skillCooldown(skillid, effect.getCooldown())); final ScheduledFuture<?> timer = TimerManager.getInstance().schedule(new CancelCooldownAction(chr, skillid), effect.getCooldown() * 1000); chr.addCooldown(skillid, System.currentTimeMillis(), effect.getCooldown() * 1000, timer); } } final ChannelCharacter player = c.getPlayer(); switch (skillid) { case 1121001: case 1221001: case 1321001: case 9001020: // GM magnet final byte number_of_mobs = reader.readByte(); reader.skip(3); for (int i = 0; i < number_of_mobs; i++) { final int mobId = reader.readInt(); final Monster mob = chr.getMap().getMonsterByOid(mobId); if (mob != null) { // chr.getMap().broadcastMessage(chr, MaplePacketCreator.showMagnet(mobId, reader.readByte()), chr.getPosition()); mob.switchController(chr, mob.isControllerHasAggro()); } } chr.getMap().broadcastMessage(chr, ChannelPackets.showBuffeffect(chr.getId(), skillid, 1, reader.readByte()), chr.getPosition()); c.write(ChannelPackets.enableActions()); break; default: Point pos = null; if (reader.remaining() == 7) { pos = reader.readVector(); } if (skill.getId() == 2311002) { // Mystic Door if (chr.canDoor()) { if (!FieldLimitType.MysticDoor.check(chr.getMap().getFieldLimit())) { effect.applyTo(player, pos); } else { c.write(ChannelPackets.enableActions()); } } else { chr.sendNotice(5, "Please wait 5 seconds before casting Mystic Door again."); c.write(ChannelPackets.enableActions()); } } else { if (StatEffect.parseMountInfo(player, skill.getId()) != 0 && player.getBuffedValue(BuffStat.MONSTER_RIDING) == null && player.getDragon() != null) { player.getMap().broadcastMessage(ChannelPackets.removeDragon(player.getId())); player.getMap().removeMapObject(player.getDragon()); player.setDragon(null); } effect.applyTo(player, pos); } break; } } public static void handleMeleeAttack(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { if (!chr.isAlive()) { chr.getCheatTracker().registerOffense(CheatingOffense.ATTACKING_WHILE_DEAD); return; } final AttackInfo attack = DamageParse.parseDmgM(reader); double maxdamage = chr.getStats().getCurrentMaxBaseDamage(); final boolean mirror = chr.getBuffedValue(BuffStat.MIRROR_IMAGE) != null; int attackCount = chr.getJobId() >= 430 && chr.getJobId() <= 434 ? 2 : 1, skillLevel = 0; StatEffect effect = null; ISkill skill = null; if (attack.skill != 0) { skill = SkillInfoProvider.getSkill(Skills.getLinkedAranSkill(attack.skill)); skillLevel = chr.getCurrentSkillLevel(skill); if (skillLevel == 0) { c.disconnect(true); return; } effect = attack.getAttackEffect(chr, skillLevel, skill); maxdamage *= effect.getDamage() / 100.0; attackCount = effect.getAttackCount(); if (effect.getCooldown() > 0) { if (chr.isInCooldown(attack.skill)) { c.write(ChannelPackets.enableActions()); return; } c.write(ChannelPackets.skillCooldown(attack.skill, effect.getCooldown())); chr.addCooldown(attack.skill, System.currentTimeMillis(), effect.getCooldown() * 1000, TimerManager.getInstance().schedule( new CancelCooldownAction(chr, attack.skill), effect.getCooldown() * 1000)); } } attackCount *= mirror ? 2 : 1; // handle combo orbconsume int numFinisherOrbs = 0; final Integer comboBuff = chr.getBuffedValue(BuffStat.COMBO); if (isFinisher(attack.skill)) { // finisher if (comboBuff != null) { numFinisherOrbs = comboBuff.intValue() - 1; } chr.handleOrbconsume(); } else if (attack.targets > 0 && comboBuff != null) { // handle combo orbgain switch (chr.getJobId()) { case 111: case 112: case 1110: case 1111: if (attack.skill != 1111008) { // shout should not give orbs chr.handleOrbgain(); } break; } } switch (chr.getJobId()) { case 511: case 512: { chr.handleEnergyCharge(5110001, attack.targets); break; } case 1510: case 1511: case 1512: { chr.handleEnergyCharge(15100004, attack.targets); break; } } // handle sacrifice hp loss if (attack.targets > 0 && attack.skill == 1211002) { // handle charged // blow final int advcharge_level = chr.getCurrentSkillLevel(SkillInfoProvider.getSkill(1220010)); if (advcharge_level > 0) { if (!SkillInfoProvider.getSkill(1220010).getEffect(advcharge_level).makeChanceResult()) { chr.cancelEffectFromBuffStat(BuffStat.WK_CHARGE); } } else { chr.cancelEffectFromBuffStat(BuffStat.WK_CHARGE); } } final ChannelCharacter player = c.getPlayer(); if (numFinisherOrbs > 0) { maxdamage *= numFinisherOrbs; } else if (comboBuff != null) { ISkill combo; if (player.getJobId() == 1110 || player.getJobId() == 1111) { combo = SkillInfoProvider.getSkill(11111001); } else { combo = SkillInfoProvider.getSkill(1111002); } maxdamage *= 1.0 + (combo.getEffect(player.getCurrentSkillLevel(combo)).getDamage() / 100.0 - 1.0) * (comboBuff.intValue() - 1); } if (isFinisher(attack.skill)) { if (numFinisherOrbs == 0) { return; } maxdamage = ChannelCharacter.getDamageCap(); // FIXME reenable // damage // calculation for // finishers } DamageParse.applyAttack(attack, skill, player, attackCount, maxdamage, effect, mirror ? AttackType.NON_RANGED_WITH_MIRROR : AttackType.NON_RANGED); chr.getMap().broadcastMessage( chr, ChannelPackets.closeRangeAttack(chr.getId(), attack.tbyte, attack.skill, skillLevel, attack.display, attack.animation, attack.speed, attack.allDamage, chr.getLevel()), chr.getPosition()); } public static void handleRangedAttack(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { if (!chr.isAlive()) { chr.getCheatTracker().registerOffense(CheatingOffense.ATTACKING_WHILE_DEAD); return; } final AttackInfo attack = DamageParse.parseDmgR(reader); int bulletCount = 1, skillLevel = 0; StatEffect effect = null; ISkill skill = null; if (attack.skill != 0) { skill = SkillInfoProvider.getSkill(attack.skill); skillLevel = chr.getCurrentSkillLevel(skill); if (skillLevel == 0) { c.disconnect(true); return; } effect = attack.getAttackEffect(chr, skillLevel, skill); switch (attack.skill) { case 21110004: // Ranged but uses attackcount instead case 14101006: // Vampure bulletCount = effect.getAttackCount(); break; default: bulletCount = effect.getBulletCount(); break; } if (effect.getCooldown() > 0) { if (chr.isInCooldown(attack.skill)) { c.write(ChannelPackets.enableActions()); return; } c.write(ChannelPackets.skillCooldown(attack.skill, effect.getCooldown())); chr.addCooldown(attack.skill, System.currentTimeMillis(), effect.getCooldown() * 1000, TimerManager.getInstance().schedule( new CancelCooldownAction(chr, attack.skill), effect.getCooldown() * 1000)); } } final Integer ShadowPartner = chr.getBuffedValue(BuffStat.SHADOWPARTNER); if (ShadowPartner != null) { bulletCount *= 2; } int projectile = 0, visProjectile = 0; if (attack.AOE != 0 && chr.getBuffedValue(BuffStat.SOULARROW) == null && attack.skill != 4111004) { projectile = chr.getUseInventory().getItem(attack.slot).getItemId(); if (attack.csstar > 0) { visProjectile = chr.getCashInventory().getItem(attack.csstar).getItemId(); } else { visProjectile = projectile; } // Handle bulletcount if (chr.getBuffedValue(BuffStat.SPIRIT_CLAW) == null) { int bulletConsume = bulletCount; if (effect != null && effect.getBulletConsume() != 0) { bulletConsume = effect.getBulletConsume() * (ShadowPartner != null ? 2 : 1); } InventoryManipulator.removeById(c, chr.getUseInventory(), projectile, bulletConsume, false, true); } } double basedamage; int projectileWatk = 0; if (projectile != 0) { projectileWatk = ItemInfoProvider.getInstance().getWatkForProjectile(projectile); } final ActivePlayerStats statst = chr.getStats(); switch (attack.skill) { case 4001344: // Lucky Seven case 4121007: // Triple Throw case 14001004: // Lucky seven case 14111005: // Triple Throw basedamage = statst.getTotalLuk() * 5.0f * (statst.getTotalWatk() + projectileWatk) / 100; break; case 4111004: // Shadow Meso // basedamage = ((effect.getMoneyCon() * 10) / 100) * effect.getProb(); // Not sure basedamage = 13000; break; default: if (projectileWatk != 0) { basedamage = statst.calculateMaxBaseDamage(statst.getTotalWatk() + projectileWatk); } else { basedamage = statst.getCurrentMaxBaseDamage(); } switch (attack.skill) { case 3101005: // arrowbomb is hardcore like that basedamage *= effect.getX() / 100.0; break; } break; } if (effect != null) { basedamage *= effect.getDamage() / 100.0; int money = effect.getMoneyCon(); if (money != 0) { if (money > chr.getMeso()) { money = chr.getMeso(); } chr.gainMeso(-money, false); } } DamageParse.applyAttack(attack, skill, chr, bulletCount, basedamage, effect, ShadowPartner != null ? AttackType.RANGED_WITH_SHADOWPARTNER : AttackType.RANGED); chr.getMap().broadcastMessage( chr, ChannelPackets.rangedAttack(chr.getId(), attack.tbyte, attack.skill, skillLevel, attack.display, attack.animation, attack.speed, visProjectile, attack.allDamage, attack.position, chr.getLevel()), chr.getPosition()); } public static void MagicDamage(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { if (!chr.isAlive()) { chr.getCheatTracker().registerOffense(CheatingOffense.ATTACKING_WHILE_DEAD); return; } final AttackInfo attack = DamageParse.parseDmgMa(reader); final ISkill skill = SkillInfoProvider.getSkill(attack.skill); final int skillLevel = chr.getCurrentSkillLevel(skill); if (skillLevel == 0) { c.disconnect(true); return; } final StatEffect effect = attack.getAttackEffect(chr, skillLevel, skill); if (effect.getCooldown() > 0) { if (chr.isInCooldown(attack.skill)) { c.write(ChannelPackets.enableActions()); return; } c.write(ChannelPackets.skillCooldown(attack.skill, effect.getCooldown())); chr.addCooldown(attack.skill, System.currentTimeMillis(), effect.getCooldown() * 1000, TimerManager.getInstance().schedule( new CancelCooldownAction(chr, attack.skill), effect.getCooldown() * 1000)); } DamageParse.applyAttackMagic(attack, skill, c.getPlayer(), effect); chr.getMap().broadcastMessage( chr, ChannelPackets.magicAttack(chr.getId(), attack.tbyte, attack.skill, skillLevel, attack.display, attack.animation, attack.speed, attack.allDamage, attack.charge, chr.getLevel()), chr.getPosition()); } public static void handleWheelOfFortuneEffect(final int itemId, final ChannelCharacter chr) { // BA 03 00 00 72 01 00 00 << extra int switch (itemId) { case 5510000: { if (!chr.isAlive()) { chr.getMap().broadcastMessage(chr, ChannelPackets.showSpecialEffect(chr.getId(), itemId), false); } break; } default: System.out.println("Unhandled item effect [WheelOfFortuneHandler] id : " + itemId); break; } } public static void handleMesoDrop(final int meso, final ChannelCharacter chr) { if (!chr.isAlive() || meso < 10 || meso > 50000 || meso > chr.getMeso()) { chr.getClient().write(ChannelPackets.enableActions()); return; } chr.gainMeso(-meso, false, true); chr.getMap().spawnMesoDrop(meso, chr.getPosition(), chr, chr, true, ItemDropType.DEFAULT); } public static void handleFaceExpression(final int emote, final ChannelCharacter chr) { if (emote > 7) { final int emoteid = 5159992 + emote; final Inventory inventory = chr.getInventoryForItem(emoteid); if (inventory.findById(emoteid) == null) { chr.getCheatTracker().registerOffense(CheatingOffense.USING_UNAVAILABLE_ITEM, Integer.toString(emoteid)); return; } } if (emote > 0) { chr.getMap().broadcastMessage(chr, ChannelPackets.facialExpression(chr, emote), false); } } public static void handleHealthRegeneration(final PacketReader reader, final ChannelCharacter chr) throws PacketFormatException { reader.skip(4); final int healHP = reader.readShort(); final int healMP = reader.readShort(); final ActivePlayerStats stats = chr.getStats(); if (stats.getHp() <= 0) { return; } if (healHP != 0) { if (healHP > stats.getHealHP()) { // chr.getCheatTracker().registerOffense(CheatingOffense.REGEN_HIGH_HP, // String.valueOf(healHP)); } chr.addHP(healHP); } if (healMP != 0) { if (healMP > stats.getHealMP()) { chr.getCheatTracker().registerOffense(CheatingOffense.REGEN_HIGH_MP, String.valueOf(healMP)); } chr.addMP(healMP); } } public static void handleMovePlayer(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { // reader.skip(5); // unknown final Point Original_Pos = chr.getPosition(); // 4 bytes Added on v.80 // MSEA reader.skip(37); // log.trace("Movement command received: unk1 {} unk2 {}", new Object[] // { unk1, unk2 }); final List<LifeMovementFragment> res = MovementParse.parseMovement(reader); if (res != null) { // TODO more validation of input data if (reader.remaining() != 18) { // System.out.println("reader.remaining != 18 (movement parsing error)"); return; } final GameMap map = c.getPlayer().getMap(); if (chr.isHidden()) { chr.setLastRes(res); } else { speedCheck(res, c); map.broadcastMessage(chr, ChannelPackets.movePlayer(chr.getId(), res, Original_Pos), false); } MovementParse.updatePosition(res, chr, 0); map.movePlayer(chr, chr.getPosition()); /* * int count = c.getPlayer().getFallCounter(); if * (map.getFootholds().findBelow(c.getPlayer().getPosition()) == * null) { if (count > 3) { c.getPlayer().changeMap(map, * map.getPortal(0)); } else { * c.getPlayer().setFallCounter(++count); } } else if (count > 0) { * c.getPlayer().setFallCounter(0); } */ } } private static void speedCheck(final List<LifeMovementFragment> res, final ChannelClient c) { double speedMod; final double playerSpeedMod = c.getPlayer().getStats().getSpeedMod() + 0.005; for (final LifeMovementFragment lmf : res) { if (lmf.getClass() == AbsoluteLifeMovement.class) { final AbsoluteLifeMovement alm = (AbsoluteLifeMovement) lmf; speedMod = Math.abs(alm.getPixelsPerSecond().x) / 125.0; if (speedMod > playerSpeedMod) { if (alm.getUnk() != 0) { if (speedMod > playerSpeedMod) { c.getPlayer().getCheatTracker().registerOffense(CheatingOffense.FAST_MOVE); break; } } } } } } public static void handleChangeMapSpecial(final String portal_name, final ChannelClient c, final ChannelCharacter chr) { final Portal portal = chr.getMap().getPortal(portal_name); // reader.skip(2); if (portal != null) { portal.enterPortal(c); } } public static void handleChangeMap(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { if (reader.remaining() != 0) { reader.skip(7); // 1 = from dying 2 = regular portals final int targetid = reader.readInt(); // FF FF FF FF final Portal portal = chr.getMap().getPortal(reader.readLengthPrefixedString()); if (reader.remaining() >= 7) { reader.skip(4); // 2F 03 04 00 } reader.skip(1); final boolean wheel = reader.readShort() > 0; final GameMapFactory mapFactory = ChannelServer.getMapFactory(); if (targetid != -1 && !chr.isAlive()) { if (chr.getEventInstance() != null) { chr.getEventInstance().revivePlayer(chr); } chr.setStance(0); if (!wheel) { chr.getStats().setHp(50); final GameMap to = chr.getMap().getReturnMap(); chr.changeMap(to, to.getPortal(0)); } else { if (chr.haveItem(5510000, 1, false, true)) { // Wheel of // Fortune chr.getStats().setHp(chr.getStats().getMaxHp() / 100 * 40); InventoryManipulator.removeById(c, chr.getCashInventory(), 5510000, 1, true, false); final GameMap to = chr.getMap(); chr.changeMap(to, to.getPortal(0)); } else { c.disconnect(); } } } else if (targetid != -1 && chr.isGM()) { final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (targetid != -1 && !chr.isGM()) { final int divi = chr.getMapId() / 100; if (divi == 9130401) { // Only allow warp if player is already // in Intro map, or else = hack if (targetid == 130000000 || targetid / 100 == 9130401) { // Cygnus // introduction final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } } else if (divi == 9140900) { // Aran Introduction if (targetid == 914090011 || targetid == 914090012 || targetid == 914090013 || targetid == 140090000) { final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } } else if (divi == 9140901 && targetid == 140000000) { c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (divi == 9140902 && (targetid == 140030000 || targetid == 140000000)) { // thing // is. // dont // really // know // which // one! c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (divi == 9000900 && targetid / 100 == 9000900 && targetid > chr.getMapId()) { final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (divi / 1000 == 9000 && targetid / 100000 == 9000) { if (targetid < 900090000 || targetid > 900090004) { // 1 // movie c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); } final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (divi / 10 == 1020 && targetid == 1020000) { // Adventurer // movie // clip // Intro c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (chr.getMapId() == 900090101 && targetid == 100030100) { c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (chr.getMapId() == 2010000 && targetid == 104000000) { c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if (chr.getMapId() == 106020001 || chr.getMapId() == 106020502) { if (targetid == chr.getMapId() - 1) { c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } } else if (chr.getMapId() == 0 && targetid == 10000) { c.write(UIPacket.IntroDisableUI(false)); c.write(UIPacket.IntroLock(false)); c.write(ChannelPackets.enableActions()); final GameMap to = mapFactory.getMap(targetid); chr.changeMap(to, to.getPortal(0)); } } else { if (portal != null) { portal.enterPortal(c); } else { c.write(ChannelPackets.enableActions()); } } } } public static void handleUseInnerPortal(final PacketReader reader, final ChannelClient c, final ChannelCharacter chr) throws PacketFormatException { final Portal portal = c.getPlayer().getMap().getPortal(reader.readLengthPrefixedString()); final Point target = reader.readVector(); if (portal == null) { c.disconnect(); return; } else if (portal.getPosition().distanceSq(chr.getPosition()) > 22500) { chr.getCheatTracker().registerOffense(CheatingOffense.USING_FARAWAY_PORTAL); } chr.getMap().movePlayer(chr, target); } }