package handling.channel.handler; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleClient; import client.MapleJob; import static client.MapleJob.getJobName; import client.PlayerStats; import client.Skill; import client.SkillFactory; import client.SkillMacro; import client.inventory.Item; import client.inventory.MapleInventoryType; import client.inventory.ModifyInventory; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; import constants.GameConstants; import constants.ItemConstants; import handling.RecvPacketOpcode; import handling.channel.ChannelServer; import static handling.channel.handler.DamageParse.NotEffectforAttack; import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.log4j.Logger; import scripting.npc.NPCScriptManager; import server.MapleInventoryManipulator; import server.MapleItemInformationProvider; import server.MaplePortal; import server.MapleStatEffect; import server.Randomizer; import server.ServerProperties; import server.events.MapleEvent; import server.events.MapleEventType; import server.events.MapleSnowball.MapleSnowballs; import server.life.MapleMonster; import server.maps.FieldLimitType; import server.maps.MapleArrowsTurret; import server.maps.MapleMap; import server.quest.MapleQuest; import server.skill.冒险家.勇士; import server.skill.冒险家.独行客; import tools.FileoutputUtil; import tools.MaplePacketCreator; import tools.data.input.LittleEndianAccessor; import tools.data.input.SeekableLittleEndianAccessor; import tools.packet.InventoryPacket; import tools.packet.MTSCSPacket; import tools.packet.SkillPacket; public class PlayerHandler { private static final Logger log = Logger.getLogger(PlayerHandler.class); public static int fox = 0; // 消耗斗气的技能 public static int isFinisher(int skillid) { switch (skillid) { case 勇士.黑暗之剑: case 勇士.黑暗之斧: return 2; case 勇士.气绝剑: case 勇士.气绝斧: return 4; } return 0; } public static void ChangeKeymap(SeekableLittleEndianAccessor slea, MapleCharacter chr) { if ((slea.available() > 8L) && (chr != null)) { slea.skip(4); int numChanges = slea.readInt(); for (int i = 0; i < numChanges; i++) { int key = slea.readInt(); byte type = slea.readByte(); int action = slea.readInt(); if ((type == 1) && (action >= 1000)) { Skill skil = SkillFactory.getSkill(action); if ((skil != null) && (((skil.isInvisible()) && (chr.getSkillLevel(skil) <= 0)) || (action % 10000 < 1000))) { continue; } } chr.changeKeybinding(key, type, action); } } else if (chr != null) { int type = slea.readInt(); int data = slea.readInt(); switch (type) { case 1: if (data <= 0) { chr.getQuestRemove(MapleQuest.getInstance(122221)); } else { chr.getQuestNAdd(MapleQuest.getInstance(122221)).setCustomData(String.valueOf(data)); } break; case 2: if (data <= 0) { chr.getQuestRemove(MapleQuest.getInstance(122223)); } else { chr.getQuestNAdd(MapleQuest.getInstance(122223)).setCustomData(String.valueOf(data)); } break; case 3: if (data <= 0) { chr.getQuestRemove(MapleQuest.getInstance(122224)); } else { chr.getQuestNAdd(MapleQuest.getInstance(122224)).setCustomData(String.valueOf(data)); } } } } // 坐椅子 public static void UseChair(int itemId, MapleClient c, MapleCharacter chr) { // 19 0A 00 // 19 FF FF 取消 if ((chr == null) || (chr.getMap() == null)) { return; } chr.setChair(itemId); c.getSession().write(MaplePacketCreator.showChair(c.getPlayer().getId(),itemId)); chr.getMap().broadcastMessage(chr, MaplePacketCreator.showChair(chr.getId(), itemId), false); c.getSession().write(MaplePacketCreator.enableActions()); } public static void CancelChair(short id, MapleClient c, MapleCharacter chr) { if (id == -1) { chr.setChair(0); c.getSession().write(MaplePacketCreator.cancelChair(-1, chr.getId())); if (chr.getMap() != null) { chr.getMap().broadcastMessage(chr, MaplePacketCreator.showChair(chr.getId(), 0), false); } } else { chr.setChair(id); c.getSession().write(MaplePacketCreator.cancelChair(id, chr.getId())); } } /** * 使用缩地石 * @param slea * @param c * @param chr */ public static void TrockAddMap(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { byte type = slea.readByte(); if (type == 0) { // 删除地图 int mapId = slea.readInt(); chr.deleteFromRegRocks(mapId); c.getSession().write(MTSCSPacket.getTrockRefresh(chr, (byte)1, true)); } else if (type == 1) { if (!FieldLimitType.VipRock.check(chr.getMap().getFieldLimit())) { chr.addRegRockMap(); c.getSession().write(MTSCSPacket.getTrockRefresh(chr, (byte)1, false)); } else { chr.dropMessage(1, "你可能没有保存此地图."); } } } public static void CharInfoRequest(int objectid, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.getMap() == null)) { return; } MapleCharacter player = chr.getMap().getCharacterById(objectid); c.getSession().write(MaplePacketCreator.enableActions()); if ((player != null)) { c.getSession().write(MaplePacketCreator.charInfo(player, chr.getId() == objectid)); } } public static void UseItemEffect(int itemId, MapleClient c, MapleCharacter chr) { if (itemId == 0) { chr.setItemEffect(0); } else { Item toUse = chr.getInventory(MapleInventoryType.CASH).findById(itemId); if ((toUse == null) || (toUse.getItemId() != itemId) || (toUse.getQuantity() < 1)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } if (itemId != 5510000) { chr.setItemEffect(itemId); } } chr.getMap().broadcastMessage(chr, MaplePacketCreator.itemEffect(chr.getId(), itemId), false); } public static void UseTitleEffect(int itemId, MapleClient c, MapleCharacter chr) { if (itemId == 0) { chr.setTitleEffect(0); chr.getQuestRemove(MapleQuest.getInstance(GameConstants.称号)); } else { Item toUse = chr.getInventory(MapleInventoryType.SETUP).findById(itemId); if ((toUse == null) || (toUse.getItemId() != itemId) || (toUse.getQuantity() < 1)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } if (itemId / 10000 == 370) { chr.setTitleEffect(itemId); chr.getQuestNAdd(MapleQuest.getInstance(GameConstants.称号)).setCustomData(String.valueOf(itemId)); } } chr.getMap().broadcastMessage(chr, MaplePacketCreator.showTitleEffect(chr.getId(), itemId), false); } public static void CancelItemEffect(int id, MapleCharacter chr) { chr.cancelEffect(MapleItemInformationProvider.getInstance().getItemEffect(-id), false, -1L); } public static void CancelBuffHandler(int sourceid, MapleCharacter chr) { if ((chr == null) || (chr.getMap() == null)) { return; } Skill skill = SkillFactory.getSkill(sourceid); if (chr.isShowPacket()) { chr.dropSpouseMessage(10, "收到取消技能BUFF 技能ID " + sourceid + " 技能名字 " + SkillFactory.getSkillName(sourceid)); } if (skill == null) { return; } if (skill.isChargeSkill()) { chr.setKeyDownSkill_Time(0L); chr.getMap().broadcastMessage(chr, MaplePacketCreator.skillCancel(chr, sourceid), false); } else { if (sourceid == 3121013) { chr.getClient().getSession().write(MaplePacketCreator.skillCancel(chr, sourceid)); } else { chr.cancelEffect(skill.getEffect(1), false, -1L); } } chr.cancelEffect(skill.getEffect(1), false, -1L); } public static void CancelMech(SeekableLittleEndianAccessor slea, MapleCharacter chr) { if (chr == null) { return; } chr.setnorba(); int sourceid = slea.readInt(); if ((sourceid % 10000 < 1000) && (SkillFactory.getSkill(sourceid) == null)) { sourceid += 1000; } Skill skill = SkillFactory.getSkill(sourceid); if (skill == null) { return; } MapleStatEffect eff = skill.getEffect(chr.getTotalSkillLevel(sourceid)); if (skill.isChargeSkill()) { chr.setKeyDownSkill_Time(0L); chr.getMap().broadcastMessage(chr, MaplePacketCreator.skillCancel(chr, sourceid), false); }else { chr.cancelEffect(skill.getEffect(slea.readByte()), false, -1L); } } public static void QuickSlot(SeekableLittleEndianAccessor slea, MapleCharacter chr) { if (chr == null) { return; } chr.getQuickSlot().resetQuickSlot(); for (int i = 0; i < 28; i++) { chr.getQuickSlot().addQuickSlot(i, slea.readInt()); } } public static void SkillEffect(SeekableLittleEndianAccessor slea, MapleCharacter chr) { // 33 39 41 40 00 05 A9 06 int skillId = slea.readInt(); byte level = slea.readByte(); byte display = slea.readByte(); byte direction = slea.readByte(); // byte speed = slea.readByte(); Point position = null; if (slea.available() == 4L) { position = slea.readPos(); } else if (slea.available() == 8) { position = slea.readPos(); } Skill skill = SkillFactory.getSkill(skillId); if ((chr == null) || (skill == null) || (chr.getMap() == null)) { return; } int skilllevel_serv = chr.getTotalSkillLevel(skill); if ((skilllevel_serv > 0) && (skilllevel_serv == level) && (skill.isChargeSkill())) { chr.setKeyDownSkill_Time(System.currentTimeMillis()); chr.getMap().broadcastMessage(chr, MaplePacketCreator.skillEffect(chr.getId(), skillId, level, display, direction, (byte)1, position), false); } } public static void specialAttack(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.getMap() == null)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } int pos_x = slea.readInt(); int pos_y = slea.readInt(); int pos_unk = slea.readInt(); int display = slea.readInt(); int skillId = slea.readInt(); boolean isLeft = slea.readByte() > 0; int speed = slea.readInt(); int tickCount = slea.readInt(); Skill skill = SkillFactory.getSkill(skillId); int skilllevel = chr.getTotalSkillLevel(skill); if (chr.isShowPacket()) { System.err.println("[SpecialAttack] - 技能ID: " + skillId + " 技能等级: " + skilllevel); } if ((skill == null) || (skilllevel <= 0)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } chr.getMap().broadcastMessage(chr, MaplePacketCreator.showBuffeffect(chr, skillId, 1, chr.getLevel(), skilllevel), false); chr.getMap().broadcastMessage(chr, MaplePacketCreator.showSpecialAttack(chr.getId(), tickCount, pos_x, pos_y, display, skillId, skilllevel, isLeft, speed), chr.getTruePosition()); } public static void AfterSkill(LittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { int skillid = slea.readInt(); if ((chr == null) || (chr.hasBlockedInventory()) || (chr.getMap() == null)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } } /** * 处理BUFF * @param slea * @param c * @param chr */ public static void SpecialSkill(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { // 31 CE CC 10 00 01 80 00 00 // 31 2B 46 0F 00 14 00 00 // 31 BC BC 21 00 04 01 A6 86 01 00 58 02 // 31 5A 43 23 00 14 49 FB 16 08 时空门 // 31 39 41 40 00 05 00 00 if ((chr == null) || (chr.hasBlockedInventory()) || (chr.getMap() == null)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } int skillid = slea.readInt(); int skillLevel = slea.readByte(); if (chr.isShowPacket()) { chr.dropMessage(5,"[SpecialSkill] - 技能ID: " + skillid + " 技能等级: " + skillLevel); } Point pos = null; if (slea.available() == 4) { pos = new Point(slea.readShort(), slea.readShort()); } Skill skill = SkillFactory.getSkill(skillid); if ((skill == null)) { chr.dropMessage(5,"[SpecialSkill] - 不存在的技能ID" + skillid); c.getSession().write(MaplePacketCreator.enableActions()); return; } skillLevel = chr.getTotalSkillLevel(skillid); MapleStatEffect effect = skill.getEffect(skillLevel); // 获取技能效果 if ((effect.getCooldown(chr) > 0) && (!chr.isGM())) { if (chr.skillisCooling(skillid)) {//TODO 修复客户端冷却时间不一致 c.getSession().write(MaplePacketCreator.enableActions()); return; } c.getSession().write(MaplePacketCreator.skillCooldown(skillid, effect.getCooldown(chr))); chr.addCooldown(skillid, System.currentTimeMillis(), effect.getCooldown(chr) * 1000); } FileoutputUtil.log("看是否有特需处理的BUFF"); if (effect.is时空门()) { FileoutputUtil.log("释放时空们"); if (!FieldLimitType.MysticDoor.check(chr.getMap().getFieldLimit())) { effect.applyTo(c.getPlayer(), pos); } else { c.getSession().write(MaplePacketCreator.enableActions()); } } else { // int mountid = MapleStatEffect.parseMountInfo(c.getPlayer(), skill.getId()); // if ((mountid != 0) && (mountid != GameConstants.getMountItem(skill.getId(), chr)) && (!chr.isIntern()) && (chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -122) == null) // && (!GameConstants.isMountItemAvailable(mountid, chr.getJob()))) { // c.getSession().write(MaplePacketCreator.enableActions()); // return; // } FileoutputUtil.log("释放技能效果!"); effect.applyTo(chr, pos); } } /** * 处理攻击包 * @param slea * @param c * @param header */ public static void 攻击处理(SeekableLittleEndianAccessor slea, MapleClient c, RecvPacketOpcode header) { MapleCharacter chr = c.getPlayer(); if (chr == null) { return; } if (chr.hasBlockedInventory() || chr.getMap() == null) { chr.dropMessage(5, "现在还不能进行攻击。"); c.getSession().write(MaplePacketCreator.enableActions()); return; } if (!chr.isAdmin() && chr.getMap().isMarketMap()) { chr.dropMessage(5, "在自由市场内无法使用技能。"); c.getSession().write(MaplePacketCreator.enableActions()); return; } switch (header) { case CLOSE_RANGE_ATTACK://近战攻击 PlayerHandler.closeRangeAttack(slea, c, chr); break; case RANGED_ATTACK://远程攻击 PlayerHandler.rangedAttack(slea, c, chr); break; case MAGIC_ATTACK://魔法攻击 PlayerHandler.MagicDamage(slea, c, chr); break; case SPECIAL_MAGIC_ATTACK: slea.skip(12); PlayerHandler.MagicDamage(slea, c, chr); break; case SUMMON_ATTACK: SummonHandler.SummonAttack(slea, c, chr); break; } chr.monsterMultiKill(); } public static void closeRangeAttack(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { PlayerHandler.closeRangeAttack(slea, c, chr, false); } public static void closeRangeAttack(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr, boolean 被动攻击) { //获取攻击信息 AttackInfo attack = DamageParse.parseCloseRangeAttack(slea, chr); if (attack == null) { chr.dropMessage(5, "攻击出现错误。"); c.getSession().write(MaplePacketCreator.enableActions()); return; } boolean mirror = chr.getBuffedValue(MapleBuffStat.影分身) != null; double maxdamage = chr.getStat().getCurrentMaxBaseDamage(); // 盾牌 Item shield = c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).getItem((short) -10); int attackCount = (shield != null) && (shield.getItemId() / 10000 == 134) ? 2 : 1; int skillLevel = 0; MapleStatEffect effect = null; Skill skill = null; //判断是不为普通攻击? if (attack.skillId != 0) { skill = SkillFactory.getSkill(attack.skillId); if (skill == null) { chr.dropMessage(5, "获取技能失败!"+attack.skillId); c.getSession().write(MaplePacketCreator.enableActions()); return; } skillLevel = chr.getTotalSkillLevel(skill); effect = attack.getAttackEffect(chr, skillLevel, skill); if (effect == null) { if (chr.isShowPacket()) { chr.dropMessage(5, "近距离攻击效果为空. 使用技能: " + skill.getId() + " - " + skill.getName() + " 技能等级: " + skillLevel); } FileoutputUtil.log(FileoutputUtil.SpecialSkill_log, "近距离攻击效果为空 玩家[" + chr.getName() + " 职业: " + getJobName(chr.getJob()) + "(" + chr.getJob() + ")] 使用技能: " + skill.getId() + " - " + skill.getName() + " 技能等级: " + skillLevel); c.getSession().write(MaplePacketCreator.enableActions()); return; } attackCount = effect.getAttackCount(chr); if (GameConstants.isEventMap(chr.getMapId())) { for (MapleEventType t : MapleEventType.values()) { MapleEvent e = ChannelServer.getInstance(chr.getClient().getChannel()).getEvent(t); if (e.isRunning() && !chr.isGM()) { for (int i : e.getType().mapids) { if (chr.getMapId() == i) { chr.dropMessage(5, "无法在这个地方使用."); return; } } } } } // if (attack.skillId == 1321013) { // maxdamage += chr.getStat().getCurrentMaxHp(); // } // // if (attack.skillId == 4100012 || attack.skillId == 4120019) { // maxdamage *= (effect.getDamage() + chr.getStat().getDamageIncrease(attack.skillId) + effect.getX() * chr.getLevel()) / 100.0D; // } else { // maxdamage *= (effect.getDamage() + chr.getStat().getDamageIncrease(attack.skillId)) / 100.0D; // } if (attack.skillId == 独行客.金钱炸弹) { chr.handleMesosbomb(attack, 0); } if (effect.getCooldown(chr) > 0 && !被动攻击) { if (chr.skillisCooling(attack.skillId) && NotEffectforAttack(attack.skillId)) { chr.dropMessage(5, "技能由于冷却时间限制,暂时无法使用。"); c.getSession().write(MaplePacketCreator.enableActions()); return; } if (!chr.skillisCooling(attack.skillId)) { c.getSession().write(MaplePacketCreator.skillCooldown(attack.skillId, effect.getCooldown(chr))); chr.addCooldown(attack.skillId, System.currentTimeMillis(), effect.getCooldown(chr) * 1000); } } } //最后处理伤害信息 attack = DamageParse.Modify_AttackCrit(attack, chr, 1, effect); //伤害次数最后计算 attackCount *= mirror ? 2 : 1; //活动,攻击雪球,普通攻击 if ((chr.getMapId() == 109060000 || chr.getMapId() == 109060002 || chr.getMapId() == 109060004) && attack.skillId == 0) { MapleSnowballs.hitSnowball(chr); } //消耗斗气的技能 if (isFinisher(attack.skillId) > 0) { int numFinisherOrbs = 0; Integer comboBuff = chr.getBuffedValue(MapleBuffStat.斗气集中); if (comboBuff != null) { numFinisherOrbs = comboBuff - 1; } if (numFinisherOrbs <= 0) { return; } chr.handleOrbconsume(isFinisher(attack.skillId)); maxdamage *= numFinisherOrbs; } //给地图上的玩家显示当前玩家使用技能效果 byte[] packet; if (被动攻击) { packet = MaplePacketCreator.passiveAttack(chr, skillLevel, 0, attack, false); } else { packet = MaplePacketCreator.closeRangeAttack(chr, skillLevel, 0, attack, false); } if (!chr.isHidden()) { chr.getMap().broadcastMessage(chr, packet, chr.getTruePosition()); } else { chr.getMap().broadcastGMMessage(chr, packet, false); } //攻击伤害处理 DamageParse.applyAttack(attack, skill, c.getPlayer(), attackCount, maxdamage, effect, mirror ? AttackType.NON_RANGED_WITH_MIRROR : AttackType.NON_RANGED, 0); } /** * 远程攻击 * @param slea * @param c * @param chr */ public static void rangedAttack(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { AttackInfo attack = DamageParse.parseRangedAttack(slea, chr); if (attack == null) { if (chr.isShowPacket()) { chr.dropSpouseMessage(25, "[RangedAttack] - 远距离攻击封包解析返回为空."); } c.getSession().write(MaplePacketCreator.enableActions()); return; } int bulletCount = 1; int skillLevel = 0; MapleStatEffect effect = null; Skill skill = null; boolean noBullet = GameConstants.isCastJob(chr.getJob()); if (attack.skillId != 0) { skill = SkillFactory.getSkill(attack.skillId); //暂时这样修改 if (skill == null) { c.getSession().write(MaplePacketCreator.enableActions()); return; } skillLevel = chr.getTotalSkillLevel(attack.skillId); effect = attack.getAttackEffect(chr, skillLevel, skill); if (effect == null) { if (chr.isShowPacket()) { chr.dropMessage(5, "近距离攻击效果为空. 使用技能: " + skill.getId() + " - " + skill.getName() + " 技能等级: " + skillLevel); } FileoutputUtil.log(FileoutputUtil.SpecialSkill_log, "远距离攻击效果为空 玩家[" + chr.getName() + " 职业: " + getJobName(chr.getJob()) + "(" + chr.getJob() + ")] 使用技能: " + skill.getId() + " - " + skill.getName() + " 技能等级: " + skillLevel); c.getSession().write(MaplePacketCreator.enableActions()); return; } if (GameConstants.isEventMap(chr.getMapId())) { for (MapleEventType eventType : MapleEventType.values()) { MapleEvent event = ChannelServer.getInstance(chr.getClient().getChannel()).getEvent(eventType); if ((event.isRunning()) && (!chr.isGM())) { for (int i : event.getType().mapids) { if (chr.getMapId() == i) { chr.dropMessage(5, "无法在这个地方使用."); return; } } } } } bulletCount = Math.max(effect.getBulletCount(chr), effect.getAttackCount(chr)); if ((effect.getCooldown(chr) > 0) && (((attack.skillId != 35111004) && (attack.skillId != 35121013)))) { if (chr.skillisCooling(attack.skillId)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } c.getSession().write(MaplePacketCreator.skillCooldown(attack.skillId, effect.getCooldown(chr))); chr.addCooldown(attack.skillId, System.currentTimeMillis(), effect.getCooldown(chr) * 1000); } } attack = DamageParse.Modify_AttackCrit(attack, chr, 2, effect); boolean mirror = chr.getBuffedValue(MapleBuffStat.影分身) != null; bulletCount *= (mirror ? 2 : 1); int projectile = 0; int visProjectile = 0; if (noBullet && (chr.getBuffedValue(MapleBuffStat.无形箭弩) == null)) { Item item = chr.getInventory(MapleInventoryType.USE).getItem(attack.starSlot); if (item == null) { return; } projectile = item.getItemId(); if (attack.cashSlot > 0) { if (chr.getInventory(MapleInventoryType.CASH).getItem(attack.cashSlot) == null) { return; } visProjectile = chr.getInventory(MapleInventoryType.CASH).getItem(attack.cashSlot).getItemId(); } else { visProjectile = projectile; } int bulletConsume = bulletCount; if ((effect != null) && (effect.getBulletConsume() != 0)) { bulletConsume = effect.getBulletConsume() * (mirror ? 2 : 1); } if ((chr.getJob() == 412) && (bulletConsume > 0) && (item.getQuantity() < MapleItemInformationProvider.getInstance().getSlotMax(projectile))) { Skill expert = SkillFactory.getSkill(4110012); if (chr.getTotalSkillLevel(expert) > 0) { MapleStatEffect eff = expert.getEffect(chr.getTotalSkillLevel(expert)); if (eff.makeChanceResult()) { item.setQuantity((short) (item.getQuantity() + 1)); c.getSession().write(InventoryPacket.modifyInventory(false, Collections.singletonList(new ModifyInventory(1, item)))); bulletConsume = 0; c.getSession().write(InventoryPacket.getInventoryStatus()); } } } if (bulletConsume > 0) { boolean useItem = true; if (chr.getBuffedValue(MapleBuffStat.子弹数量) != null) { int count = chr.getBuffedIntValue(MapleBuffStat.子弹数量) - bulletConsume; if (count >= 0) { chr.setBuffedValue(MapleBuffStat.子弹数量, count); useItem = false; } else { chr.cancelEffectFromBuffStat(MapleBuffStat.子弹数量); bulletConsume += count; } } if ((useItem) && (!MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, projectile, bulletConsume, false, true))) { chr.dropMessage(5, "您的箭/子弹/飞镖不足。"); return; } } } int projectileWatk = 0; if (projectile != 0) { projectileWatk = MapleItemInformationProvider.getInstance().getWatkForProjectile(projectile); } PlayerStats statst = chr.getStat(); double basedamage = statst.getCurrentMaxBaseDamage() + statst.calculateMaxProjDamage(projectileWatk, chr); switch (attack.skillId) { case 3101005: if (effect == null) { break; } basedamage *= effect.getX() / 100.0D; } if (effect != null) { basedamage *= (effect.getDamage() + statst.getDamageIncrease(attack.skillId)) / 100.0D; long money = effect.getMoneyCon(); if (money != 0) { if (money > chr.getMeso()) { money = chr.getMeso(); } chr.gainMeso(-money, false); } } if (!chr.isHidden()) { chr.getMap().broadcastMessage(chr, MaplePacketCreator.rangedAttack(chr, skillLevel, visProjectile, attack), chr.getTruePosition()); } else { chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.rangedAttack(chr, skillLevel, visProjectile, attack), false); } DamageParse.applyAttack(attack, skill, chr, bulletCount, basedamage, effect, mirror ? AttackType.RANGED_WITH_SHADOWPARTNER : AttackType.RANGED, visProjectile); } /** * 魔法攻击 * @param slea * @param c * @param chr */ public static void MagicDamage(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { AttackInfo attack = DamageParse.parseMagicDamage(slea, chr); if (attack == null) { c.getSession().write(MaplePacketCreator.enableActions()); return; } Skill skill = SkillFactory.getSkill(attack.skillId); if (skill == null) { c.getSession().write(MaplePacketCreator.enableActions()); return; } int skillLevel = chr.getTotalSkillLevel(skill); MapleStatEffect effect = attack.getAttackEffect(chr, skillLevel, skill); if (effect == null) { if (chr.isShowPacket()) { chr.dropMessage(5, "魔法攻击效果为空. 使用技能: " + skill.getId() + " - " + skill.getName() + " 技能等级: " + skillLevel); } FileoutputUtil.log(FileoutputUtil.SpecialSkill_log, "魔法攻击效果为空 玩家[" + chr.getName() + " 职业: " + getJobName(chr.getJob()) + "(" + chr.getJob() + ")] 使用技能: " + skill.getId() + " - " + skill.getName() + " 技能等级: " + skillLevel); return; } attack = DamageParse.Modify_AttackCrit(attack, chr, 3, effect); if (GameConstants.isEventMap(chr.getMapId())) { for (MapleEventType t : MapleEventType.values()) { MapleEvent e = ChannelServer.getInstance(chr.getClient().getChannel()).getEvent(t); if ((e.isRunning()) && (!chr.isGM())) { for (int i : e.getType().mapids) { if (chr.getMapId() == i) { chr.dropMessage(5, "无法在这个地方使用."); return; } } } } } double maxdamage = chr.getStat().getCurrentMaxBaseDamage() * (effect.getDamage() + chr.getStat().getDamageIncrease(attack.skillId)) / 100.0D; if (GameConstants.isPyramidSkill(attack.skillId)) { maxdamage = 1.0D; } else if ((GameConstants.is新手职业(skill.getId() / 10000)) && (skill.getId() % 10000 == 1000)) { maxdamage = 40.0D; } if (effect.getCooldown(chr) > 0) { if (chr.skillisCooling(attack.skillId)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } c.getSession().write(MaplePacketCreator.skillCooldown(attack.skillId, effect.getCooldown(chr))); chr.addCooldown(attack.skillId, System.currentTimeMillis(), effect.getCooldown(chr) * 1000); } if (!chr.isHidden()) { chr.getMap().broadcastMessage(chr, MaplePacketCreator.magicAttack(chr, skillLevel, 0, attack), chr.getTruePosition()); } else { chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.magicAttack(chr, skillLevel, 0, attack), false); } DamageParse.applyAttackMagic(attack, skill, c.getPlayer(), effect, maxdamage); } /** * 玩家丢钱出来 * @param meso * @param chr */ public static void DropMeso(int meso, MapleCharacter chr) { if ((!chr.isAlive()) || (meso < 10) || (meso > 50000) || (meso > chr.getMeso())) { chr.getClient().getSession().write(MaplePacketCreator.enableActions()); return; } chr.gainMeso(-meso, false, true); chr.getMap().spawnMesoDrop(meso, chr.getTruePosition(), chr, chr, true, (byte) 0); //chr.getCheatTracker().checkDrop(true); } public static void ChangeEmotion(int emote, MapleCharacter chr) { if (emote > 7) { int emoteid = 5159992 + emote; MapleInventoryType type = ItemConstants.getInventoryType(emoteid); if (chr.getInventory(type).findById(emoteid) == null) { return; } } if ((emote > 0) && (chr != null) && (chr.getMap() != null) && (!chr.isHidden())) { chr.getMap().broadcastMessage(chr, MaplePacketCreator.facialExpression(chr, emote), false); } } /** * 自动恢复HP/MP * @param slea * @param chr */ public static void Heal(SeekableLittleEndianAccessor slea, MapleCharacter chr) { // 2F 00 14 00 00 0A 00 00 00 00 8E 9D 5C 00 if (chr == null) { return; } slea.skip(4); int healHP = slea.readShort(); int healMP = slea.readShort(); PlayerStats stats = chr.getStat(); if (stats.getHp() <= 0 || healHP > 100 || healMP > 100) { return; } long now = System.currentTimeMillis(); if ((healHP != 0) && (chr.canHP(now + 1000L))) { // if (healHP > stats.getHealHP()) { // healHP = (int) stats.getHealHP(); // } chr.addHP(healHP); } if ((healMP != 0) && (chr.canMP(now + 1000L))) { // @TODO 限制每次恢复的MP // if (healMP > stats.getHealMP()) { // healMP = (int) stats.getHealMP(); // } chr.addMP(healMP); } } /** * 玩家移动 * @param slea * @param c * @param chr */ public static void MovePlayer(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { if (chr == null) { return; } slea.readByte(); Point Original_Pos = chr.getPosition(); slea.readShort(); // position slea.readShort(); List res; try { res = MovementParse.parseMovement(slea, 1, chr); } catch (ArrayIndexOutOfBoundsException e) { FileoutputUtil.log("AIOBE Type1:\r\n" + slea.toString(true)); return; } if ((res != null) && (chr.getMap() != null)) { if (slea.available() != 10) { FileoutputUtil.log("玩家" + chr.getName() + "(" + MapleJob.getName(MapleJob.getById(chr.getJob())) + ") slea.available != 8 (角色移动出错) 剩余封包长度: " + slea.available()); FileoutputUtil.log(FileoutputUtil.Movement_Char, "slea.available != 8 (角色移动出错) 封包: " + slea.toString(true)); return; } MapleMap map = c.getPlayer().getMap(); slea.skip(2); if (chr.isHidden()) { chr.setLastRes(res); chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.movePlayer(chr.getId(), slea), false); } else { chr.getMap().broadcastMessage(chr, MaplePacketCreator.movePlayer(chr.getId(), slea), false); } MovementParse.updatePosition(res, chr, 0); Point pos = chr.getTruePosition(); map.movePlayer(chr, pos); } } /** * 请求传送口数据 * @param portal_name * @param c * @param chr */ public static void ChangeMapSpecial(String portal_name, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.getMap() == null)) { return; } MaplePortal portal = chr.getMap().getPortal(portal_name); if ((portal != null) && (!chr.hasBlockedInventory())) { portal.enterPortal(c); } else { c.getSession().write(MaplePacketCreator.enableActions()); } } /** * 更换地图 * @param slea * @param c * @param chr */ public static void ChangeMap(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { // 15 00 00 00 00 00 00 00 00 00 角色回城 // 15 08 00 00 00 00 00 00 00 00 死亡后回程 if ((chr == null) || (chr.getMap() == null)) { return; } if (slea.available() != 0L) { int type = slea.readByte(); int targetid = slea.readInt(); FileoutputUtil.log("换地图目标:"+targetid); MaplePortal portal = chr.getMap().getPortal(slea.readMapleAsciiString()); if ((targetid != -1) && (!chr.isAlive())) { // 角色死亡 chr.setStance(0); if ((chr.getEventInstance() != null) && (chr.getEventInstance().revivePlayer(chr)) && (chr.isAlive())) { FileoutputUtil.log("没有死亡但是要回程:"+targetid); return; } chr.getStat().setHp(50); MapleMap to = chr.getMap().getReturnMap(); FileoutputUtil.log("死亡后准备回程::"+to.getId()); chr.changeMap(to, to.getPortal(0)); } else if (targetid != -1 && c.getPlayer().isGM()) { MapleMap to = ChannelServer.getInstance(c.getChannel()).getMapFactory().getMap(targetid); MaplePortal pto = to.getPortal(0); chr.changeMap(to, pto); } else if ((targetid != -1) ) { MapleMap to = ChannelServer.getInstance(c.getChannel()).getMapFactory().getMap(targetid); chr.changeMap(to, to.getPortal(0)); } else if ((portal != null) && (!chr.hasBlockedInventory())) { FileoutputUtil.log("执行传送口脚本:"+portal.getScriptName()); portal.enterPortal(c); } else { c.getSession().write(MaplePacketCreator.enableActions()); } } } public static void InnerPortal(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.getMap() == null)) { return; } MaplePortal portal = chr.getMap().getPortal(slea.readMapleAsciiString()); int toX = slea.readShort(); int toY = slea.readShort(); if (portal == null) { return; } if ((portal.getPosition().distanceSq(chr.getTruePosition()) > 22500.0D) && (!chr.isGM())) { return; } chr.getMap().movePlayer(chr, new Point(toX, toY)); } public static void snowBall(SeekableLittleEndianAccessor slea, MapleClient c) { c.getSession().write(MaplePacketCreator.enableActions()); } public static void leftKnockBack(SeekableLittleEndianAccessor slea, MapleClient c) { if (c.getPlayer().getMapId() / 10000 == 10906) { c.getSession().write(MaplePacketCreator.leftKnockBack()); c.getSession().write(MaplePacketCreator.enableActions()); } } public static void ReIssueMedal(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { int questId = slea.readShort(); int itemId = slea.readInt(); MapleQuest quest = MapleQuest.getInstance(questId); if ((((quest != null ? 1 : 0) & (quest.getMedalItem() > 0 ? 1 : 0)) != 0) && (chr.getQuestStatus(quest.getId()) == 2) && (quest.getMedalItem() == itemId)) { if (!chr.haveItem(itemId)) { int price = 100; int infoQuestId = 29949; String infoData = "count=1"; if (chr.containsInfoQuest(infoQuestId, "count=")) { String line = chr.getInfoQuest(infoQuestId); String[] splitted = line.split("="); if (splitted.length == 2) { int data = Integer.parseInt(splitted[1]); infoData = "count=" + data + 1; if (data == 1) { price = 1000; } else if (data == 2) { price = 10000; } else if (data == 3) { price = 100000; } else { price = 1000000; } } else { chr.dropMessage(1, "重新领取勋章出现错误"); c.getSession().write(MaplePacketCreator.enableActions()); return; } } if (chr.getMeso() < price) { chr.dropMessage(1, "本次重新需要金币: " + price + "\r\n请检查金币是否足够"); c.getSession().write(MaplePacketCreator.enableActions()); return; } chr.gainMeso(-price, true, true); MapleInventoryManipulator.addById(c, itemId, (short) 1, ""); c.getSession().write(MaplePacketCreator.updateMedalQuestInfo((byte) 0, itemId)); } else { c.getSession().write(MaplePacketCreator.updateMedalQuestInfo((byte) 3, itemId)); } } else { c.getSession().write(MaplePacketCreator.enableActions()); } } public static void PlayerUpdate(MapleCharacter chr) { // boolean autoSave = true; if (chr == null || chr.getMap() == null) { return; } long startTime = System.currentTimeMillis(); chr.saveToDB(false, false); if (chr.isShowPacket()) { chr.dropMessage(-11, "保存数据,耗时 " + (System.currentTimeMillis() - startTime) + " 毫秒"); } } public static void LoadPlayerSuccess(MapleClient c, MapleCharacter chr) { if (chr == null || chr.getMap() == null) { return; } String msg = "欢迎来到#b蓝蜗牛区(仿官方)#k\r\n\r\n祝您玩的愉快!"; c.getSession().write(MaplePacketCreator.sendHint(msg, 250, 5)); int exp = c.getChannelServer().getExpRate(c.getWorld()); if (exp > 1) { chr.dropSpouseMessage(20, "[系统提示] 当前服务器处于"+exp+"倍经验活动中,祝您玩的愉快!"); } if (c.getChannelServer().getAutoGain() >= 2) { chr.dropSpouseMessage(25, "[系统提示] 在线时间奖励双倍活动正在举行."); } c.getSession().write(MaplePacketCreator.showPlayerCash(chr)); /*if ((GameConstants.is新骑士团(chr.getJob())) && (chr.getLevel() >= 10)) { if (chr.getPQLog("骑士团能力修复", 1) == 0) { chr.resetStats(4, 4, 4, 4, true); chr.setPQLog("骑士团能力修复", 1); } if (chr.getPQLog("骑士团技能修复", 1) == 0) { chr.SpReset(); chr.setPQLog("骑士团技能修复", 1); } }*/ // if ((player.getJob() == 6001) && (player.getLevel() < 10)) { // while (player.getLevel() < 10) { // player.gainExp(5000, true, false, true); // } // } // if (!player.isIntern()) { // if ((c.getChannelServer().isCheckCash()) && ((player.getItemQuantity(4000463) >= 800) || (player.getCSPoints(-1) >= 900000)) && (player.getHyPay(3) < 200)) { // String msgtext = "玩家 "+player.getName()+" 数据异常,服务器自动断开他的连接。"+" 国庆币数量: "+player.getItemQuantity(4000463)+" 点券总额: "+player.getCSPoints(-1); // WorldBroadcastService.getInstance().broadcastGMMessage(MaplePacketCreator.serverNotice(6, "[GM Message] "+msgtext)); // FileoutputUtil.log("日志\\数据异常.log", msgtext); // c.getSession().close(true); // } else if ((c.getChannelServer().isCheckSp()) && (player.checkMaxStat())) { // String msgtext = "玩家 "+player.getName()+" 属性点异常,服务器自动断开他的连接。当前角色总属性点为: "+player.getPlayerStats()+" 职业: "+player.getJob()+" 等级: "+player.getLevel(); // WorldBroadcastService.getInstance().broadcastGMMessage(MaplePacketCreator.serverNotice(6, "[GM Message] "+msgtext)); // FileoutputUtil.log("日志\\数据异常.log", msgtext); // c.getSession().close(true); // } // } } public static void ChangeMarketMap(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.getMap() == null) || (chr.hasBlockedInventory())) { c.getSession().write(MaplePacketCreator.enableActions()); return; } int chc = slea.readByte() + 1; int toMapId = slea.readInt(); if ((toMapId >= 910000001) && (toMapId <= 910000022)) { if (c.getChannel() != chc) { if (chr.getMapId() != toMapId) { MapleMap to = ChannelServer.getInstance(chc).getMapFactory().getMap(toMapId); chr.setMap(to); chr.changeChannel(chc); } else { chr.changeChannel(chc); } } else { MapleMap to = ChannelServer.getInstance(c.getChannel()).getMapFactory().getMap(toMapId); chr.changeMap(to, to.getPortal(0)); } } else { c.getSession().write(MaplePacketCreator.enableActions()); } } public static boolean isBossMap(int mapid) { switch (mapid) { case 0: case 105100300: case 105100400: case 211070100: case 211070101: case 211070110: case 220080001: case 240040700: case 240060200: case 240060201: case 270050100: case 271040100: case 271040200: case 280030000: case 280030001: case 280030100: case 300030310: case 551030200: case 802000111: case 802000211: case 802000311: case 802000411: case 802000611: case 802000711: case 802000801: case 802000802: case 802000803: case 802000821: case 802000823: return true; } return false; } public static void showPlayerCash(SeekableLittleEndianAccessor slea, MapleClient c) { int accId = slea.readInt(); int playerId = slea.readInt(); } public static void quickBuyCashShopItem(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.getMap() == null)) { //c.getSession().write(MaplePacketCreator.enableActions()); return; } int accId = slea.readInt(); int playerId = slea.readInt(); int mode = slea.readInt(); int cssn = slea.readInt(); int toCharge = slea.readByte() == 1 ? 1 : 2; if ((chr.getId() != playerId) || (chr.getAccountID() != accId)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } switch (mode) { case 10: if ((chr.getCSPoints(toCharge) >= 600) && (chr.getStorage().getSlots() < 93)) { chr.modifyCSPoints(toCharge, -600, false); chr.getStorage().increaseSlots((byte) 4); chr.getStorage().saveToDB(); c.getSession().write(MaplePacketCreator.playerCashUpdate(mode, toCharge, chr)); } else { chr.dropMessage(5, "扩充失败,点券余额不足或者仓库栏位已超过上限。"); } break; case 11: case 12: case 13: case 14: case 15: int iv = mode == 15 ? 5 : mode == 14 ? 4 : mode == 13 ? 3 : mode == 12 ? 2 : mode == 11 ? 1 : -1; if (iv > 0) { MapleInventoryType tpye = MapleInventoryType.getByType((byte) iv); if ((chr.getCSPoints(toCharge) >= 600) && (chr.getInventory(tpye).getSlotLimit() < 93)) { chr.modifyCSPoints(toCharge, -600, false); chr.getInventory(tpye).addSlot((byte) 4); c.getSession().write(MaplePacketCreator.playerCashUpdate(mode, toCharge, chr)); } else { chr.dropMessage(1, "扩充失败,点券余额不足或者栏位已超过上限。"); } } else { chr.dropMessage(1, "扩充失败,扩充的类型不正确。"); } } } public static void GUIDE_TRANSFER(LittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { slea.skip(2); int mapid = slea.readInt(); chr.changeMap(ChannelServer.getInstance(c.getChannel()).getMapFactory().getMap(mapid)); switch (mapid) { case 102000003: NPCScriptManager.getInstance().start(c, 10202); break; } } public static void SpawnArrowsTurret(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.hasBlockedInventory()) || (chr.getMap() == null)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } byte side = slea.readByte(); Point pos = new Point(); pos.x = slea.readInt(); pos.y = slea.readInt(); for (MapleArrowsTurret AT : chr.getMap().getAllArrowsTurrets()) { if (AT.getOwnerId() == chr.getId()) { chr.getMap().removeMapObject(AT); chr.getMap().broadcastMessage(SkillPacket.cancelArrowsTurret(AT)); break; } } MapleArrowsTurret tospawn = new MapleArrowsTurret(chr, side, pos); chr.getMap().spawnArrowsTurret(tospawn); } public static void ArrowsTurretAttack(SeekableLittleEndianAccessor slea, MapleClient c, MapleCharacter chr) { if ((chr == null) || (chr.hasBlockedInventory()) || (chr.getMap() == null)) { c.getSession().write(MaplePacketCreator.enableActions()); return; } int startime = (int) System.currentTimeMillis(); int type = slea.readShort(); if (type == 4) { slea.skip(4);//cid slea.skip(4);//unk slea.skip(4);//time? int skillid = slea.readInt(); int side = slea.readByte(); slea.skip(1); int slevel = slea.readShort(); int unk = slea.readInt(); Point pos = new Point(); Point spoint = new Point(); spoint.x = slea.readInt(); spoint.y = slea.readInt(); boolean sp = skillid != 3121013; if (sp) { for (MapleArrowsTurret AT : chr.getMap().getAllArrowsTurrets()) { if (AT.getOwnerId() == chr.getId()) { pos = AT.getPosition(); break; } } } else { pos = chr.getOldPosition(); } chr.getMap().broadcastMessage(chr, SkillPacket.ArrowsTurretAttack(chr.getId(), chr.getMapId(), slevel, pos, side, spoint, sp), false); } } public static void getMonoid(SeekableLittleEndianAccessor slea, MapleCharacter chr) { int count = slea.readInt(); int skill = slea.readInt(); List<Integer> oid = new ArrayList(); for (int i = 0; i < count; i++) { slea.skip(5); oid.add(slea.readInt()); slea.skip(4); } switch (skill) { default: FileoutputUtil.log(FileoutputUtil.获取未处理被动技能, "技能ID:" + skill + " 循环次数:" + count + " 封包: " + slea.toString(true)); } } public static void UpdateMacrSkill(SeekableLittleEndianAccessor slea, MapleCharacter chr) { slea.skip(1); int index = slea.readByte(); int skillid = slea.readInt(); int old = 0; int t = 1; String newstr; if (slea.available() > 0) { old = slea.readByte(); t = 2; } for (int i = 0; i < t; i++) { String date = chr.getQuestNAdd(MapleQuest.getInstance(52554)).getCustomData(); if (date == null) { date = "cmd0=0;cmd1=0;cmd2=0"; } String[] StrArray = date.split(";"); index = i == 0 ? index : old; skillid = i == 0 ? skillid : 0; StrArray[index] = "cmd" + index + "=" + skillid; newstr = StrArray[0] + ";" + StrArray[1] + ";" + StrArray[2]; chr.send(MaplePacketCreator.UpdateMacrSkill(index, skillid)); MapleQuest.getInstance(52554).forceStart(chr, 0, newstr); } } /** * * @param slea * @param c */ public static void handleCharInfo(SeekableLittleEndianAccessor slea, MapleClient c) { } }