包頭recvops

搜索：

# 自動回復HP/MP
HEAL_OVER_TIME = 0x9C

替換：

# 自動回復HP/MP
HEAL_OVER_TIME = 0x9C

# 連結技能
TEACH_SKILL = 0x9E


源碼部分

搜索：

    REPORT,
    MOB_BOMB,

替換：

    REPORT,
    TEACH_SKILL,//連結技能
    MOB_BOMB,

搜索：

            case REPORT:
                PlayersHandler.Report(slea, c);
                break;

替換：

            case REPORT:
                PlayersHandler.Report(slea, c);
                break;
            case TEACH_SKILL://連結技能
                PlayersHandler.TeachSkill(slea, c, c.getPlayer());
                break;


搜索：

    public static boolean inArea(MapleCharacter chr) {
        for (Rectangle rect : chr.getMap().getAreas()) {
            if (rect.contains(chr.getTruePosition())) {
                return true;
            }
        }
        for (MapleMist mist : chr.getMap().getAllMistsThreadsafe()) {
            if (mist.getOwnerId() == chr.getId() && mist.isPoisonMist() == 2 && mist.getBox().contains(chr.getTruePosition())) {
                return true;
            }
        }
        return false;
    }
}

替換：

    public static boolean inArea(MapleCharacter chr) {
        for (Rectangle rect : chr.getMap().getAreas()) {
            if (rect.contains(chr.getTruePosition())) {
                return true;
            }
        }
        for (MapleMist mist : chr.getMap().getAllMistsThreadsafe()) {
            if (mist.getOwnerId() == chr.getId() && mist.isPoisonMist() == 2 && mist.getBox().contains(chr.getTruePosition())) {
                return true;
            }
        }
        return false;
    }

    public static void TeachSkill(LittleEndianAccessor slea, MapleClient c, MapleCharacter chr) {
        if ((chr == null) || (chr.getMap() == null) || (chr.hasBlockedInventory())) {
            c.getSession().write(MaplePacketCreator.enableActions());
            return;
        }
        if (chr.getLevel() < 70) {
            chr.dropMessage(1, "達到70級後，可以在同一伺服器中\r\n選擇一個角色傳授該技能");
            c.getSession().write(MaplePacketCreator.enableActions());
            return;
        }
        int skillId = slea.readInt();
        if (chr.getSkillLevel(skillId) < 1) {
            c.getSession().write(MaplePacketCreator.enableActions());
            return;
        }
        int toChrId = slea.readInt();
        Pair toChrInfo = MapleCharacterUtil.getNameById(toChrId, 0);
        if (toChrInfo == null) {
            c.getSession().write(MaplePacketCreator.enableActions());
            return;
        }
        int toChrAccId = ((Integer) toChrInfo.getRight()).intValue();
        String toChrName = (String) toChrInfo.getLeft();
        MapleQuest quest = MapleQuest.getInstance(7783);
        if ((quest != null) && (chr.getAccountID() == toChrAccId)) {
            int toSkillId;
            if (GameConstants.isCannon(chr.getJob())) {
                toSkillId = 80000000;
            } else {
                if (GameConstants.isDemon(chr.getJob())) {
                    toSkillId = 80000001;
                } else {
                    if (GameConstants.isMercedes(chr.getJob())) {
                        toSkillId = 80001040;
                } else {
                    chr.dropMessage(1, "傳授技能失敗");
                        c.getSession().write(MaplePacketCreator.enableActions());
                        return;
                    }
                }
            }
            if ((chr.teachSkill(toSkillId, toChrId) > 0) && (toSkillId >= 80000000)) {
                chr.changeTeachSkill(skillId, toChrId);
                quest.forceComplete(chr, 0);
                c.getSession().write(MaplePacketCreator.teachMessage(skillId, toChrId, toChrName));
            } else {
                chr.dropMessage(1, new StringBuilder().append("傳授技能失敗角色[").append(toChrName).append("]已經獲得該技能").toString());
            }
        } else {
            chr.dropMessage(1, "傳授技能失敗。");
        }
        c.getSession().write(MaplePacketCreator.enableActions());
    }
}

頁首添加

import client.MapleCharacterUtil;


搜索：

    public final void equipChanged() {
        if (map == null) {
            return;
        }
        map.broadcastMessage(this, EtcPacket.updateCharLook(this), false);
        stats.recalcLocalStats(this);
        if (getMessenger() != null) {
            World.Messenger.updateMessenger(getMessenger().getId(), getName(), client.getChannel());
        }
    }

替換：

    public final void equipChanged() {
        if (map == null) {
            return;
        }
        map.broadcastMessage(this, EtcPacket.updateCharLook(this), false);
        stats.recalcLocalStats(this);
        if (getMessenger() != null) {
            World.Messenger.updateMessenger(getMessenger().getId(), getName(), client.getChannel());
        }
    }

    public void changeTeachSkill(int skillId, int toChrId) {//链接技能
        Skill skill = SkillFactory.getSkill(skillId);
        if (skill == null) {
            return;
        }
        this.client.getSession().write(MaplePacketCreator.updateSkill(skillId, toChrId, 1, -1L));
        this.skills.put(skill, new SkillEntry(1, (byte) 1, -1L, toChrId));
        this.changed_skills = true;
    }

    public int teachSkill(int skillId, int toChrId) {
        try {
            Connection con = DatabaseConnection.getConnection();
            PreparedStatement ps = con.prepareStatement("DELETE FROM skills WHERE skillid = ? AND teachId = ?");
            ps.setInt(1, skillId);
            ps.setInt(2, this.id);
            ps.executeUpdate();
            ps.close();
            ps = con.prepareStatement("SELECT * FROM skills WHERE skillid = ? AND characterid = ?");
            ps.setInt(1, skillId);
            ps.setInt(2, toChrId);
            ResultSet rs = ps.executeQuery();
            if (!rs.next()) {
                rs.close();
                ps.close();
                PreparedStatement psskills = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration, teachId) VALUES (?, ?, ?, ?, ?, ?)");
                psskills.setInt(1, toChrId);
                psskills.setInt(2, skillId);
                psskills.setInt(3, 1);
                psskills.setByte(4, (byte) 1);
                psskills.setLong(5, -1);
                psskills.setInt(6, this.id);
                psskills.executeUpdate();
                psskills.close();
                return 1;
            }
            rs.close();
            ps.close();
            return -1;
        } catch (Exception Ex) {
          //log.error("Error while read bosslog.", Ex);
        }
        return -1;
    }


搜索：

    public static final String makeMapleReadable(final String in) {
        String wui = in.replace('I', 'i');
        wui = wui.replace('l', 'L');
        wui = wui.replace("rn", "Rn");
        wui = wui.replace("vv", "Vv");
        wui = wui.replace("VV", "Vv");
        return wui;
    }

替換：

    public static final String makeMapleReadable(final String in) {
        String wui = in.replace('I', 'i');
        wui = wui.replace('l', 'L');
        wui = wui.replace("rn", "Rn");
        wui = wui.replace("vv", "Vv");
        wui = wui.replace("VV", "Vv");
        return wui;
    }

    public static Pair<String, Integer> getNameById(int chrId, int world) {
        try {
            Connection con = DatabaseConnection.getConnection();
            PreparedStatement ps = con.prepareStatement("SELECT * FROM characters WHERE id = ? AND world = ?");
            ps.setInt(1, chrId);
            ps.setInt(2, world);
            ResultSet rs = ps.executeQuery();
            if (!rs.next()) {
                rs.close();
                ps.close();
                return null;
            }
            Pair id = new Pair(rs.getString("name"), Integer.valueOf(rs.getInt("accountid")));
            rs.close();
            ps.close();
            return id;
        } catch (Exception e) {
            //log.error("error 'getInfoByName' " + e);
        }
        return null;
    }

搜索：

public class SkillEntry implements Serializable {

    private static final long serialVersionUID = 9179541993413738569L;
    public int skillevel;
    public byte masterlevel;
    public long expiration;

    public SkillEntry(final int skillevel, final byte masterlevel, final long expiration) {
        this.skillevel = skillevel;
        this.masterlevel = masterlevel;
        this.expiration = expiration;
    }
}

替換：

public class SkillEntry implements Serializable {

    private static final long serialVersionUID = 9179541993413738569L;
    public int skillevel;
    public byte masterlevel;
    public long expiration;
    public int teachId;
    
    public SkillEntry(final int skillevel, final byte masterlevel, final long expiration) {
        this.skillevel = skillevel;
        this.masterlevel = masterlevel;
        this.expiration = expiration;
    }

    public SkillEntry(int skillevel, byte masterlevel, long expiration, int teachId) {
        this.skillevel = skillevel;
        this.masterlevel = masterlevel;
        this.expiration = expiration;
        this.teachId = teachId;
    }
}


搜索：

    public static byte[] updateAreaInfo(int area, String info) {
        final tools.data.output.MaplePacketLittleEndianWriter mplew = new tools.data.output.MaplePacketLittleEndianWriter();
        mplew.writeShort(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
        mplew.write(0x0C);
        mplew.writeShort(area);//infoNumber
        mplew.writeMapleAsciiString(info);
        return mplew.getPacket();
    }


替換：

    public static byte[] updateAreaInfo(int area, String info) {
        final tools.data.output.MaplePacketLittleEndianWriter mplew = new tools.data.output.MaplePacketLittleEndianWriter();
        mplew.writeShort(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
        mplew.write(0x0C);
        mplew.writeShort(area);//infoNumber
        mplew.writeMapleAsciiString(info);
        return mplew.getPacket();
    }

    public static byte[] teachMessage(int skillId, int toChrId, String toChrName) {//链接技能
        MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
        mplew.writeShort(SendPacketOpcode.GIVE_CHARACTER_SKILL.getValue());
        mplew.writeInt(0);
        mplew.writeInt(skillId);
        mplew.writeInt(toChrId);
        mplew.writeMapleAsciiString(toChrName);

        return mplew.getPacket();
    }


搜索：

    SKILL_EFFECT,
    CANCEL_SKILL_EFFECT,

替換：

    SKILL_EFFECT,
    CANCEL_SKILL_EFFECT,
    GIVE_CHARACTER_SKILL,




搜索：

                ps = con.prepareStatement("SELECT skillid, skilllevel, masterlevel, expiration FROM skills WHERE characterid = ?");
                ps.setInt(1, charid);
                rs = ps.executeQuery();
                Skill skil;
                while (rs.next()) {
                    final int skid = rs.getInt("skillid");
                    skil = SkillFactory.getSkill(skid);
                    int skl = rs.getInt("skilllevel");
                    byte msl = rs.getByte("masterlevel");
                    if (skil != null && GameConstants.isApplicableSkill(skid)) {
                        if (skl > skil.getMaxLevel() && skid < 92000000) {
                            if (!skil.isBeginnerSkill() && skil.canBeLearnedBy(ret.job) && !skil.isSpecialSkill()) {
                                ret.remainingSp[GameConstants.getSkillBookForSkill(skid)] += (skl - skil.getMaxLevel());
                            }
                            skl = (byte) skil.getMaxLevel();
                        }
                        if (msl > skil.getMaxLevel()) {
                            msl = (byte) skil.getMaxLevel();
                        }
                        ret.skills.put(skil, new SkillEntry(skl, msl, rs.getLong("expiration")));

替換：

                ps = con.prepareStatement("SELECT skillid, skilllevel, masterlevel, expiration, teachId FROM skills WHERE characterid = ?");
                ps.setInt(1, charid);
                rs = ps.executeQuery();
                Skill skil;
                while (rs.next()) {
                    final int skid = rs.getInt("skillid");
                    skil = SkillFactory.getSkill(skid);
                    int skl = rs.getInt("skilllevel");
                    byte msl = rs.getByte("masterlevel");
                    int teachId = rs.getInt("teachId");
                    if (skil != null && GameConstants.isApplicableSkill(skid)) {
                        if (skl > skil.getMaxLevel() && skid < 92000000) {
                            if (!skil.isBeginnerSkill() && skil.canBeLearnedBy(ret.job) && !skil.isSpecialSkill()) {
                                ret.remainingSp[GameConstants.getSkillBookForSkill(skid)] += (skl - skil.getMaxLevel());
                            }
                            skl = (byte) skil.getMaxLevel();
                        }
                        if (msl > skil.getMaxLevel()) {
                            msl = (byte) skil.getMaxLevel();
                        }
                        ret.skills.put(skil, new SkillEntry(skl, msl, rs.getLong("expiration"), teachId));


搜索：

            ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)");
            ps.setInt(1, chr.id);

            for (final Entry<Skill, SkillEntry> skill : chr.skills.entrySet()) {
                if (GameConstants.isApplicableSkill(skill.getKey().getId())) { //do not save additional skills
                    ps.setInt(2, skill.getKey().getId());
                    ps.setInt(3, skill.getValue().skillevel);
                    ps.setByte(4, skill.getValue().masterlevel);
                    ps.setLong(5, skill.getValue().expiration);
                    ps.execute();
                }
            }

替換：

            ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration, teachId) VALUES (?, ?, ?, ?, ?, ?)");
            ps.setInt(1, chr.id);

            for (final Entry<Skill, SkillEntry> skill : chr.skills.entrySet()) {
                if (GameConstants.isApplicableSkill(skill.getKey().getId())) { //do not save additional skills
                    ps.setInt(2, skill.getKey().getId());
                    ps.setInt(3, skill.getValue().skillevel);
                    ps.setByte(4, skill.getValue().masterlevel);
                    ps.setLong(5, skill.getValue().expiration);
                    ps.setInt(6, skill.getValue().teachId);
                    ps.execute();
                }
            }


搜索：

            if (changed_skills) {
                deleteWhereCharacterId(con, "DELETE FROM skills WHERE characterid = ?");
                ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration) VALUES (?, ?, ?, ?, ?)");
                ps.setInt(1, id);

                for (final Entry<Skill, SkillEntry> skill : skills.entrySet()) {
                    if (GameConstants.isApplicableSkill(skill.getKey().getId())) { //do not save additional skills
                        ps.setInt(2, skill.getKey().getId());
                        ps.setInt(3, skill.getValue().skillevel);
                        ps.setByte(4, skill.getValue().masterlevel);
                        ps.setLong(5, skill.getValue().expiration);
                        ps.execute();
                    }
                }

替換：

            if (changed_skills) {
                deleteWhereCharacterId(con, "DELETE FROM skills WHERE characterid = ?");
                ps = con.prepareStatement("INSERT INTO skills (characterid, skillid, skilllevel, masterlevel, expiration, teachId) VALUES (?, ?, ?, ?, ?, ?)");
                ps.setInt(1, id);

                for (final Entry<Skill, SkillEntry> skill : skills.entrySet()) {
                    if (GameConstants.isApplicableSkill(skill.getKey().getId())) { //do not save additional skills
                        ps.setInt(2, skill.getKey().getId());
                        ps.setInt(3, skill.getValue().skillevel);
                        ps.setByte(4, skill.getValue().masterlevel);
                        ps.setLong(5, skill.getValue().expiration);
                        ps.setInt(6, ((SkillEntry) skill.getValue()).teachId);
                        ps.execute();
                    }
                }


搜索：

    public static final void addSkillInfo(final MaplePacketLittleEndianWriter mplew, final MapleCharacter chr) { // 0x100
        final Map<Skill, SkillEntry> skills = chr.getSkills();
        boolean useOld = skills.size() < 500;
        mplew.write(useOld ? 1 : 0); // To handle the old skill system or something? 
        if (useOld) {
            mplew.writeShort(skills.size());
            for (final Entry<Skill, SkillEntry> skill : skills.entrySet()) {
                mplew.writeInt(skill.getKey().getId());
                mplew.writeInt(skill.getValue().skillevel);
                addExpirationTime(mplew, skill.getValue().expiration);

                if (skill.getKey().isFourthJob()) {
                    mplew.writeInt(skill.getValue().masterlevel);
                }
            }
        } else {
            final Map<Integer, Integer> skillsWithoutMax = new LinkedHashMap<Integer, Integer>();
            final Map<Integer, Long> skillsWithExpiration = new LinkedHashMap<Integer, Long>();
            final Map<Integer, Byte> skillsWithMax = new LinkedHashMap<Integer, Byte>();

            // Fill in these maps
            for (final Entry<Skill, SkillEntry> skill : skills.entrySet()) {
                skillsWithoutMax.put(skill.getKey().getId(), skill.getValue().skillevel);
                if (skill.getValue().expiration > 0) {
                    skillsWithExpiration.put(skill.getKey().getId(), skill.getValue().expiration);
                }
                if (skill.getKey().isFourthJob()) {
                    skillsWithMax.put(skill.getKey().getId(), skill.getValue().masterlevel);
                }
            }

            int amount = skillsWithoutMax.size();
            mplew.writeShort(amount);
            for (final Entry<Integer, Integer> x : skillsWithoutMax.entrySet()) {
                mplew.writeInt(x.getKey());
                mplew.writeInt(x.getValue()); // 80000000, 80000001, 80001040 show cid if linked.
            }
            mplew.writeShort(0); // For each, int

            amount = skillsWithExpiration.size();
            mplew.writeShort(amount);
            for (final Entry<Integer, Long> x : skillsWithExpiration.entrySet()) {
                mplew.writeInt(x.getKey());
                mplew.writeLong(x.getValue()); // Probably expiring skills here
            }
            mplew.writeShort(0); // For each, int

            amount = skillsWithMax.size();
            mplew.writeShort(amount);
            for (final Entry<Integer, Byte> x : skillsWithMax.entrySet()) {
                mplew.writeInt(x.getKey());
                mplew.writeInt(x.getValue());
            }
            mplew.writeShort(0); // For each, int (Master level = 0? O.O)
        }
    }


替換：

    public static final void addSkillInfo(final MaplePacketLittleEndianWriter mplew, final MapleCharacter chr) { // 0x100
        final Map<Skill, SkillEntry> skills = chr.getSkills();
        mplew.write(1);
        mplew.writeShort(skills.size());
        for (Entry<Skill, SkillEntry> skill : skills.entrySet()) {
            mplew.writeInt(skill.getKey().getId());
            if (skill.getKey().getId() == 80000000 || skill.getKey().getId() == 80000001 || skill.getKey().getId() == 80001040) {//顯示傳授技能的角色
                mplew.writeInt(skill.getValue().teachId);
            } else if (skill.getKey().getId() == 110 || skill.getKey().getId() == 20021110 || skill.getKey().getId() == 30010112) {//過濾已傳授技能的角色
                mplew.writeInt(skill.getValue().teachId > 0 ? skill.getValue().teachId : skill.getValue().skillevel);
            } else {
                mplew.writeInt(skill.getValue().skillevel);
            }
            addExpirationTime(mplew, skill.getValue().expiration);
            if (skill.getKey().isFourthJob()) {
                mplew.writeInt(skill.getValue().masterlevel);
            }
        }
    }