diff --git a/.gitignore b/.gitignore index c477dc6a51..232f549d1e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,10 @@ /tools/MapleBossHpBarFetcher/build/ /tools/MapleBossHpBarFetcher/dist/ +/tools/MapleCashDropFetcher/nbproject/private/ +/tools/MapleCashDropFetcher/build/ +/tools/MapleCashDropFetcher/dist/ + /tools/MapleCouponInstaller/build/ /tools/MapleCouponInstaller/dist/ /tools/MapleCouponInstaller/nbproject/private/ diff --git a/README.md b/README.md index 2dbaad7385..7e3b4d68ad 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,15 @@ Server files: https://github.com/ronancpl/HeavenMS Client files & general tools: https://drive.google.com/drive/folders/0BzDsHSr-0V4MYVJ0TWIxd05hYUk -Recommended localhost: https://hostr.co/r5QDmhlxpp8M +Recommended localhost: https://hostr.co/fuzm4X9j7TWh -* MapleSilver's starting on window-mode, with some string fixes. This is a variation of Fraysa's https://hostr.co/gJbLZITRVHmv +* Current revision: 'n' problem fixed and removed caps for WATK, WDEF, MDEF, ACC, AVOID. + + * 'n' problem fixed https://hostr.co/r5QDmhlxpp8M + + * Fraysa's https://hostr.co/gJbLZITRVHmv + + * MapleSilver's starting on window-mode --- ### Support us diff --git a/docs/feature_list.md b/docs/feature_list.md index 749dd01f3c..39984d3ac4 100644 --- a/docs/feature_list.md +++ b/docs/feature_list.md @@ -118,6 +118,7 @@ External tools: * MapleArrowFetcher - Updates min/max quantity dropped on all arrows drop data, calculations based on mob level and whether it's a boss or not. * MapleBossHpBarFetcher - Searches the quest WZ files and reports in all relevant data regarding mobs that has a boss HP bar whilst not having a proper "boss" label. +* MapleCashDropFetcher - Searches the DB for any CASH drop data entry and lists them on a report file. * MapleCouponInstaller - Retrieves coupon info from the WZ and makes a SQL table with it. The server will use that table to gather info regarding rates and intervals. * MapleIdRetriever - Two behaviors: generates a SQL table with relation (id, name) of the handbook given as input. Given a file with names, outputs a file with ids. * MapleInvalidItemIdFetcher - Generates a file listing all inexistent itemid's currently laying on the DB. @@ -140,4 +141,9 @@ Project: * Heavily reviewed future task management inside the project. Way less trivial schedules are spawned now, relieving task overload on the TimerManager. * ThreadTracker: embedded auditing tool for run-time deadlock scanning throughout the server source (relies heavily on memory usage, designed only for debugging purposes). +Localhost: + +* Removed the 'n' problem within NPC dialog. +* Removed caps for MATK, WDEF, MDEF, ACC and AVOID. + --------------------------- \ No newline at end of file diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 22dc2cf5fe..82be832739 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -859,4 +859,11 @@ Corrigido NPC de guild tirando mesos do jogador sem efetuar a ação alguma caso 11 - 12 Março 2018, Localhost melhorado: retirado caps de Matk, Mdef, Wdef, Acc e Avoid. Balanceado Ninja Ambush, agora dando uma quantidade de dano justificável. -Implementado questline do Dyle. \ No newline at end of file +Implementado questline do Dyle. +Corrigido possível exploit com sistema de quests, onde jogador podia começar e completar quaisquer quests livremente. +Nova ferramenta: MapleCashDropFetcher. Aplicação busca por drop data de cash na DB e reporta. + +13 Merço 2018, +Adicionado feature de anúncio de mudança de classe. +Adicionado drops faltando da questline Puppeteer de Aran. +Movimentação de GM rank para alguns comandos. \ No newline at end of file diff --git a/scripts/npc/PupeteerPassword.js b/scripts/npc/PupeteerPassword.js index 78e414d5d5..462f8157fb 100644 --- a/scripts/npc/PupeteerPassword.js +++ b/scripts/npc/PupeteerPassword.js @@ -18,10 +18,17 @@ function action(mode, type, selection){ if(status == 0){ + if(cm.isQuestStarted(21728)) { + cm.sendOk("You search for any hints of the Puppeteer, but it seems a powerful force blocks the path... Better return to #b#p1061019##k."); + cm.setQuestProgress(21728, 0, 1); + cm.dispose(); + return; + } + cm.sendGetText("A suspicious voice pierces through the silence. #bPassword#k!"); } else if(status == 1){ - if(cm.getText() == "Francis is a genius Puppeteer!"){ + if(cm.getText() == "Francis is a genius Puppeteer!"){ if(cm.isQuestStarted(20730) && cm.getQuestProgress(20730, 9300285) == 0) cm.warp(910510001, 1); else if(cm.isQuestStarted(21731) && cm.getQuestProgress(21731, 9300346) == 0) diff --git a/scripts/npc/commands.js b/scripts/npc/commands.js index 20f6e5bb40..a355847ffb 100644 --- a/scripts/npc/commands.js +++ b/scripts/npc/commands.js @@ -152,6 +152,7 @@ function writeSolaxiaCommandsLv2() { //JrGM comm_cursor = comm_lv2; desc_cursor = desc_lv2; + addCommand("whereami", ""); addCommand("hide", ""); addCommand("unhide", ""); addCommand("sp", ""); @@ -188,10 +189,13 @@ function writeSolaxiaCommandsLv1() { //Donator comm_cursor = comm_lv1; desc_cursor = desc_lv1; + addCommand("bosshp", ""); + addCommand("mobhp", ""); + addCommand("whatdropsfrom", ""); + addCommand("whodrops", ""); addCommand("buffme", ""); addCommand("goto", ""); addCommand("recharge", ""); - addCommand("whereami", ""); } function writeSolaxiaCommandsLv0() { //Common @@ -204,21 +208,21 @@ function writeSolaxiaCommandsLv0() { //Common addCommand("credits", ""); addCommand("uptime", ""); addCommand("gacha", ""); - addCommand("whatdropsfrom", ""); - addCommand("whodrops", ""); addCommand("dispose", ""); addCommand("equiplv", ""); addCommand("showrates", ""); addCommand("rates", ""); addCommand("online", ""); addCommand("gm", ""); - addCommand("bug", ""); + addCommand("reportbug", ""); //addCommand("points", ""); addCommand("joinevent", ""); addCommand("leaveevent", ""); - addCommand("bosshp", ""); - addCommand("mobhp", ""); addCommand("ranks", ""); + addCommand("str", ""); + addCommand("int", ""); + addCommand("luk", ""); + addCommand("dex", ""); } function writeSolaxiaCommands() { diff --git a/scripts/quest/21728.js b/scripts/quest/21728.js new file mode 100644 index 0000000000..88bb7ba1fe --- /dev/null +++ b/scripts/quest/21728.js @@ -0,0 +1,49 @@ +/* + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +var status = -1; + +function end(mode, type, selection) { + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(qm.getQuestProgress(21728, 0) == 0) { + qm.sendNext("You haven't found the #rPuppeteer's cave#k yet, did you?"); + } else { + qm.sendNext("Hm, so the entrance is blocked by a powerful force? I see, gimme a time to think now..."); + qm.gainExp(200 * qm.getPlayer().getExpRate()); + qm.forceCompleteQuest(); + } + + qm.dispose(); + } + } +} \ No newline at end of file diff --git a/scripts/quest/21729.js b/scripts/quest/21729.js new file mode 100644 index 0000000000..f56e2b7172 --- /dev/null +++ b/scripts/quest/21729.js @@ -0,0 +1,44 @@ +/* + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +var status = -1; + +function start(mode, type, selection) { + if (mode == -1) { + qm.dispose(); + } else { + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + qm.sendNext("Okay, you should not return to #bTru#k for further details on your next steps. ... Oh wait!! I remembered something. See the #rMysterious Statue#k over there? That statue has it's origins unknwown, and there's something scribbled onto it that resembles something big, it probably is the password for the cave? #rGet the password there#k, it may help you on your journey."); + qm.forceStartQuest(); + + qm.dispose(); + } + } +} diff --git a/scripts/quest/2214.js b/scripts/quest/2214.js index b1f2f33075..10ade811f9 100644 --- a/scripts/quest/2214.js +++ b/scripts/quest/2214.js @@ -25,6 +25,8 @@ Quest ID: 2214 */ +importPackage(java.util); + var status = -1; function end(mode, type, selection) { @@ -42,6 +44,13 @@ function end(mode, type, selection) { status--; if (status == 0) { + var hourDay = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + if(!(hourDay >= 17 && hourDay < 20)) { + qm.sendNext("(Hmm, I'm searching the trash can but can't find the #t4031894# JM was talking about, maybe it's not time yet...)"); + qm.dispose(); + return; + } + if(!qm.canHold(4031894, 1)) { qm.sendNext("(Eh, I can't hold the #t4031894# right now, I need an ETC slot available.)"); qm.dispose(); diff --git a/scripts/quest/2215.js b/scripts/quest/2215.js index edb50c6a13..bea586d10e 100644 --- a/scripts/quest/2215.js +++ b/scripts/quest/2215.js @@ -25,6 +25,8 @@ Quest ID: 2215 */ +importPackage(java.util); + var status = -1; function end(mode, type, selection) { @@ -42,6 +44,13 @@ function end(mode, type, selection) { status--; if (status == 0) { + var hourDay = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); + if(!(hourDay >= 17 && hourDay < 20)) { + qm.sendNext("(Hmm, I'm searching the trash can but can't find the #t4031894# JM was talking about, maybe it's not time yet...)"); + qm.dispose(); + return; + } + if(qm.getMeso() < 2000) { qm.sendNext("(Oh, I don't have the combined fee amount yet.)"); qm.dispose(); diff --git a/sql/db_database.sql b/sql/db_database.sql index ddfc68660b..b4775a491b 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -10830,9 +10830,6 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`, (10608, 9302010, 2022524, 1, 1, 0, 100000), (10609, 9400256, 4032192, 1, 1, 0, 50000), (10610, 9400257, 4032192, 1, 1, 0, 50000), -(10611, 9410066, 5490001, 1, 1, 0, 700000), -(10612, 9410066, 5490001, 1, 1, 0, 700000), -(10613, 9410066, 5490000, 1, 1, 0, 300000), (10614, 9410066, 4000306, 1, 1, 0, 700000), (10615, 9410066, 4000306, 1, 1, 0, 700000), (10616, 9410066, 4000306, 1, 1, 0, 700000), diff --git a/sql/db_drops.sql b/sql/db_drops.sql index 27e0fadcca..2a83061591 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -1107,7 +1107,6 @@ USE `heavenms`; (9300063, 1052101, 1, 1, 0, 700), (9300082, 1052101, 1, 1, 0, 700), (9400503, 1052101, 1, 1, 0, 40000), -(2100100, 5240005, 1, 1, 0, 7000), (2100100, 4003004, 1, 1, 0, 7000), (2100100, 2000001, 1, 1, 0, 40000), (2100100, 2000003, 1, 1, 0, 40000), @@ -10022,7 +10021,6 @@ USE `heavenms`; (9500326, 1372000, 1, 1, 0, 40000), (9500345, 1372000, 1, 1, 0, 40000), (9303004, 1372000, 1, 1, 0, 700), -(6130103, 1702131, 1, 1, 0, 700), (6130103, 2000006, 1, 1, 0, 40000), (6130103, 2000004, 1, 1, 0, 40000), (6130103, 2040401, 1, 1, 0, 750), @@ -20183,7 +20181,11 @@ USE `heavenms`; (9400114, 2022063, 10, 30, 0, 200000), (9400114, 2022064, 10, 30, 0, 200000), (9400120, 4000094, 1, 1, 0, 400000), -(9400122, 4000094, 1, 1, 0, 400000); +(9400122, 4000094, 1, 1, 0, 400000), +(1110130, 4032317, 1, 1, 21717, 40000), +(1110130, 4032318, 1, 1, 21718, 40000), +(1140130, 4032319, 1, 1, 21723, 100000), +(2230131, 4032321, 1, 1, 21727, 20000); # (dropperid, itemid, minqty, maxqty, questid, chance) diff --git a/src/client/BuddyList.java b/src/client/BuddyList.java index c2e3dc0a11..1a5d77b626 100644 --- a/src/client/BuddyList.java +++ b/src/client/BuddyList.java @@ -26,10 +26,12 @@ import java.sql.ResultSet; import java.sql.Connection; import java.sql.SQLException; import java.util.Collection; +import java.util.Collections; import java.util.Deque; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.Map; +import net.server.PlayerStorage; import tools.DatabaseConnection; import tools.MaplePacketCreator; @@ -50,15 +52,22 @@ public class BuddyList { } public boolean contains(int characterId) { - return buddies.containsKey(Integer.valueOf(characterId)); + synchronized(buddies) { + return buddies.containsKey(Integer.valueOf(characterId)); + } } public boolean containsVisible(int characterId) { - BuddylistEntry ble = buddies.get(characterId); + BuddylistEntry ble; + synchronized(buddies) { + ble = buddies.get(characterId); + } + if (ble == null) { return false; } return ble.isVisible(); + } public int getCapacity() { @@ -70,42 +79,65 @@ public class BuddyList { } public BuddylistEntry get(int characterId) { - return buddies.get(Integer.valueOf(characterId)); + synchronized(buddies) { + return buddies.get(Integer.valueOf(characterId)); + } } public BuddylistEntry get(String characterName) { String lowerCaseName = characterName.toLowerCase(); - for (BuddylistEntry ble : buddies.values()) { + for (BuddylistEntry ble : getBuddies()) { if (ble.getName().toLowerCase().equals(lowerCaseName)) { return ble; } } + return null; } public void put(BuddylistEntry entry) { - buddies.put(Integer.valueOf(entry.getCharacterId()), entry); + synchronized(buddies) { + buddies.put(Integer.valueOf(entry.getCharacterId()), entry); + } } public void remove(int characterId) { - buddies.remove(Integer.valueOf(characterId)); + synchronized(buddies) { + buddies.remove(Integer.valueOf(characterId)); + } } public Collection getBuddies() { - return buddies.values(); + synchronized(buddies) { + return Collections.unmodifiableCollection(buddies.values()); + } } public boolean isFull() { - return buddies.size() >= capacity; + synchronized(buddies) { + return buddies.size() >= capacity; + } } public int[] getBuddyIds() { - int buddyIds[] = new int[buddies.size()]; - int i = 0; - for (BuddylistEntry ble : buddies.values()) { - buddyIds[i++] = ble.getCharacterId(); + synchronized(buddies) { + int buddyIds[] = new int[buddies.size()]; + int i = 0; + for (BuddylistEntry ble : buddies.values()) { + buddyIds[i++] = ble.getCharacterId(); + } + return buddyIds; + } + } + + public void broadcast(byte[] packet, PlayerStorage pstorage) { + for(int bid : getBuddyIds()) { + MapleCharacter chr = pstorage.getCharacterById(bid); + + if(chr != null && chr.isLoggedin() && !chr.isAwayFromWorld()) { + chr.announce(packet); + } } - return buddyIds; } public void loadFromDb(int characterId) { diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 25f7109243..1904fc7e83 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -199,7 +199,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private int expRate = 1, mesoRate = 1, dropRate = 1, expCoupon = 1, mesoCoupon = 1, dropCoupon = 1; private int omokwins, omokties, omoklosses, matchcardwins, matchcardties, matchcardlosses; private int owlSearch; - private int married; private long lastfametime, lastUsedCashItem, lastHealed, lastMesoDrop = -1, jailExpiration = -1; private transient int localmaxhp, localmaxmp, localstr, localdex, localluk, localint_, magic, watk; private boolean hidden, canDoor = true, berserk, hasMerchant, whiteChat = false; @@ -1093,6 +1092,36 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } createDragon(); } + + if(ServerConstants.USE_ANNOUNCE_CHANGEJOB) { + if(gmLevel > 1) { + broadcastAcquaintances(6, "[" + GameConstants.ordinal(GameConstants.getJobBranch(newJob)) + " Job] " + name + " has just become a " + newJob.name() + "."); + } + } + } + + public void broadcastAcquaintances(int type, String message) { + broadcastAcquaintances(MaplePacketCreator.serverNotice(type, message)); + } + + public void broadcastAcquaintances(byte[] packet) { + buddylist.broadcast(packet, client.getWorldServer().getPlayerStorage()); + + if(family != null) { + //family.broadcast(packet, id); not yet implemented + } + + MapleGuild guild = getGuild(); + if(guild != null) { + guild.broadcast(packet, id); + } + + /* + if(partnerid > 0) { + partner.announce(packet); not yet implemented + } + */ + announce(packet); } public void changeKeybinding(int key, MapleKeyBinding keybinding) { @@ -3955,10 +3984,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return marriageRing; } - public int getMarried() { - return married; - } - public int getMasterLevel(Skill skill) { if (skills.get(skill) == null) { return 0; diff --git a/src/client/command/Commands.java b/src/client/command/Commands.java index b3fcea7ad7..ed77335191 100644 --- a/src/client/command/Commands.java +++ b/src/client/command/Commands.java @@ -351,7 +351,7 @@ public class Commands { case "playercommands": c.getAbstractPlayerInteraction().openNpc(9201143, "commands"); break; - + case "droplimit": int dropCount = c.getPlayer().getMap().getDroppedItemCount(); if(((float) dropCount) / ServerConstants.ITEM_LIMIT_ON_MAP < 0.75f) { @@ -402,90 +402,14 @@ public class Commands { } break; } - String output = "The #b" + gachaName + "#k Gachapon contains the following items.\r\n\r\n"; + String talkStr = "The #b" + gachaName + "#k Gachapon contains the following items.\r\n\r\n"; for (int i = 0; i < 2; i++){ for (int id : gacha.getItems(i)){ - output += "-" + MapleItemInformationProvider.getInstance().getName(id) + "\r\n"; + talkStr += "-" + MapleItemInformationProvider.getInstance().getName(id) + "\r\n"; } } - output += "\r\nPlease keep in mind that there are items that are in all gachapons and are not listed here."; - c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, output, "00 00", (byte) 0)); - break; - - case "whatdropsfrom": - if (sub.length < 2) { - player.dropMessage(5, "Please do @whatdropsfrom "); - break; - } - String monsterName = joinStringFrom(sub, 1); - output = ""; - int limit = 3; - Iterator> listIterator = MapleMonsterInformationProvider.getMobsIDsFromName(monsterName).iterator(); - for (int i = 0; i < limit; i++) { - if(listIterator.hasNext()) { - Pair data = listIterator.next(); - int mobId = data.getLeft(); - String mobName = data.getRight(); - output += mobName + " drops the following items:\r\n\r\n"; - for (MonsterDropEntry drop : MapleMonsterInformationProvider.getInstance().retrieveDrop(mobId)){ - try { - String name = MapleItemInformationProvider.getInstance().getName(drop.itemId); - if (name.equals("null") || drop.chance == 0){ - continue; - } - float chance = 1000000 / drop.chance / player.getDropRate(); - output += "- " + name + " (1/" + (int) chance + ")\r\n"; - } catch (Exception ex){ - ex.printStackTrace(); - continue; - } - } - output += "\r\n"; - } - } - c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, output, "00 00", (byte) 0)); - break; - - case "whodrops": - if (sub.length < 2) { - player.dropMessage(5, "Please do @whodrops "); - break; - } - String searchString = joinStringFrom(sub, 1); - output = ""; - listIterator = MapleItemInformationProvider.getInstance().getItemDataByName(searchString).iterator(); - if(listIterator.hasNext()) { - int count = 1; - while(listIterator.hasNext() && count <= 3) { - Pair data = listIterator.next(); - output += "#b" + data.getRight() + "#k is dropped by:\r\n"; - try { - Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE itemid = ? LIMIT 50"); - ps.setInt(1, data.getLeft()); - ResultSet rs = ps.executeQuery(); - while(rs.next()) { - String resultName = MapleMonsterInformationProvider.getMobNameFromID(rs.getInt("dropperid")); - if (resultName != null) { - output += resultName + ", "; - } - } - rs.close(); - ps.close(); - con.close(); - } catch (Exception e) { - player.dropMessage(6, "There was a problem retrieving the required data. Please try again."); - e.printStackTrace(); - break; - } - output += "\r\n\r\n"; - count++; - } - } else { - player.dropMessage(5, "The item you searched for doesn't exist."); - break; - } - c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, output, "00 00", (byte) 0)); + talkStr += "\r\nPlease keep in mind that there are items that are in all gachapons and are not listed here."; + c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, talkStr, "00 00", (byte) 0)); break; case "dispose": @@ -560,7 +484,7 @@ public class Commands { player.dropMessage(5, tips[Randomizer.nextInt(tips.length)]); break; - case "bug": + case "reportbug": if (sub.length < 2) { player.dropMessage(5, "Message too short and not sent. Please do @bug "); @@ -636,30 +560,6 @@ public class Commands { } break; - case "bosshp": - for(MapleMonster monster : player.getMap().getMonsters()) { - if(monster != null && monster.isBoss() && monster.getHp() > 0) { - long percent = monster.getHp() * 100L / monster.getMaxHp(); - String bar = "["; - for (int i = 0; i < 100; i++){ - bar += i < percent ? "|" : "."; - } - bar += "]"; - player.yellowMessage(monster.getName() + " (" + monster.getId() + ") has " + percent + "% HP left."); - player.yellowMessage("HP: " + bar); - } - } - break; - - case "mobhp": - for(MapleMonster monster : player.getMap().getMonsters()) { - if(monster != null && monster.getHp() > 0) { - player.yellowMessage(monster.getName() + " (" + monster.getId() + ") has " + monster.getHp() + " / " + monster.getMaxHp() + " HP."); - - } - } - break; - case "ranks": PreparedStatement ps = null; ResultSet rs = null; @@ -691,6 +591,47 @@ public class Commands { } } break; + + // stat autoassigning command credited to HeliosMS dev team + case "str": + case "int": + case "luk": + case "dex": + int amount = (sub.length > 1) ? Integer.parseInt(sub[1]) : player.getRemainingAp(); + boolean str = sub[0].equalsIgnoreCase("str"); + boolean Int = sub[0].equalsIgnoreCase("int"); + boolean luk = sub[0].equalsIgnoreCase("luk"); + boolean dex = sub[0].equalsIgnoreCase("dex"); + + if (amount > 0 && amount <= player.getRemainingAp() && amount <= 32763 || amount < 0 && amount >= -32763 && Math.abs(amount) + player.getRemainingAp() <= 32767) { + if (str && amount + player.getStr() <= 32767 && amount + player.getStr() >= 4) { + player.setStr(player.getStr() + amount); + player.updateSingleStat(MapleStat.STR, player.getStr()); + player.setRemainingAp(player.getRemainingAp() - amount); + player.updateSingleStat(MapleStat.AVAILABLEAP, player.getRemainingAp()); + } else if (Int && amount + player.getInt() <= 32767 && amount + player.getInt() >= 4) { + player.setInt(player.getInt() + amount); + player.updateSingleStat(MapleStat.INT, player.getInt()); + player.setRemainingAp(player.getRemainingAp() - amount); + player.updateSingleStat(MapleStat.AVAILABLEAP, player.getRemainingAp()); + } else if (luk && amount + player.getLuk() <= 32767 && amount + player.getLuk() >= 4) { + player.setLuk(player.getLuk() + amount); + player.updateSingleStat(MapleStat.LUK, player.getLuk()); + player.setRemainingAp(player.getRemainingAp() - amount); + player.updateSingleStat(MapleStat.AVAILABLEAP, player.getRemainingAp()); + } else if (dex && amount + player.getDex() <= 32767 && amount + player.getDex() >= 4) { + player.setDex(player.getDex() + amount); + player.updateSingleStat(MapleStat.DEX, player.getDex()); + player.setRemainingAp(player.getRemainingAp() - amount); + player.updateSingleStat(MapleStat.AVAILABLEAP, player.getRemainingAp()); + } else { + player.dropMessage("Please make sure the stat you are trying to raise is not over 32,767 or under 4."); + } + } else { + player.dropMessage("Please make sure your AP is not over 32,767 and you have enough to distribute."); + } + + break; default: return false; @@ -702,8 +643,113 @@ public class Commands { public static boolean executeHeavenMsCommandLv1(Channel cserv, Server srv, MapleClient c, String[] sub) { //Donator MapleCharacter player = c.getPlayer(); - switch(sub[0]) { + switch(sub[0]) { + case "bosshp": + for(MapleMonster monster : player.getMap().getMonsters()) { + if(monster != null && monster.isBoss() && monster.getHp() > 0) { + long percent = monster.getHp() * 100L / monster.getMaxHp(); + String bar = "["; + for (int i = 0; i < 100; i++){ + bar += i < percent ? "|" : "."; + } + bar += "]"; + player.yellowMessage(monster.getName() + " (" + monster.getId() + ") has " + percent + "% HP left."); + player.yellowMessage("HP: " + bar); + } + } + break; + + case "mobhp": + for(MapleMonster monster : player.getMap().getMonsters()) { + if(monster != null && monster.getHp() > 0) { + player.yellowMessage(monster.getName() + " (" + monster.getId() + ") has " + monster.getHp() + " / " + monster.getMaxHp() + " HP."); + + } + } + break; + + case "whatdropsfrom": + if (sub.length < 2) { + player.dropMessage(5, "Please do @whatdropsfrom "); + break; + } + String monsterName = joinStringFrom(sub, 1); + String output = ""; + int limit = 3; + Iterator> listIterator = MapleMonsterInformationProvider.getMobsIDsFromName(monsterName).iterator(); + for (int i = 0; i < limit; i++) { + if(listIterator.hasNext()) { + Pair data = listIterator.next(); + int mobId = data.getLeft(); + String mobName = data.getRight(); + output += mobName + " drops the following items:\r\n\r\n"; + for (MonsterDropEntry drop : MapleMonsterInformationProvider.getInstance().retrieveDrop(mobId)){ + try { + String name = MapleItemInformationProvider.getInstance().getName(drop.itemId); + if (name.equals("null") || drop.chance == 0){ + continue; + } + float chance = 1000000 / drop.chance / player.getDropRate(); + output += "- " + name + " (1/" + (int) chance + ")\r\n"; + } catch (Exception ex){ + ex.printStackTrace(); + continue; + } + } + output += "\r\n"; + } + } + c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, output, "00 00", (byte) 0)); + break; + + case "whodrops": + if (sub.length < 2) { + player.dropMessage(5, "Please do @whodrops "); + break; + } + String searchString = joinStringFrom(sub, 1); + output = ""; + listIterator = MapleItemInformationProvider.getInstance().getItemDataByName(searchString).iterator(); + if(listIterator.hasNext()) { + int count = 1; + while(listIterator.hasNext() && count <= 3) { + Pair data = listIterator.next(); + output += "#b" + data.getRight() + "#k is dropped by:\r\n"; + try { + Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE itemid = ? LIMIT 50"); + ps.setInt(1, data.getLeft()); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + String resultName = MapleMonsterInformationProvider.getMobNameFromID(rs.getInt("dropperid")); + if (resultName != null) { + output += resultName + ", "; + } + } + rs.close(); + ps.close(); + con.close(); + } catch (Exception e) { + player.dropMessage(6, "There was a problem retrieving the required data. Please try again."); + e.printStackTrace(); + break; + } + output += "\r\n\r\n"; + count++; + } + } else { + player.dropMessage(5, "The item you searched for doesn't exist."); + break; + } + c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, output, "00 00", (byte) 0)); + break; + case "buffme": + if(!player.isGM()) { + player.dropMessage(5, "You are already dead."); + break; + } + //GM Skills : Haste(Super) - Holy Symbol - Bless - Hyper Body - Echo of Hero SkillFactory.getSkill(4101004).getEffect(SkillFactory.getSkill(4101004).getMaxLevel()).applyTo(player); SkillFactory.getSkill(2311003).getEffect(SkillFactory.getSkill(2311003).getMaxLevel()).applyTo(player); @@ -753,7 +799,20 @@ public class Commands { } player.dropMessage(5, "USE Recharged."); break; - + + default: + return false; + } + + return true; + } + + public static boolean executeHeavenMsCommandLv2(Channel cserv, Server srv, MapleClient c, String[] sub) { //JrGM + MapleCharacter player = c.getPlayer(); + MapleCharacter victim; + Skill skill; + + switch(sub[0]) { case "whereami": player.yellowMessage("Map ID: " + player.getMap().getId()); player.yellowMessage("Players on this map:"); @@ -777,21 +836,8 @@ public class Commands { } } } - break; - - default: - return false; - } - - return true; - } - - public static boolean executeHeavenMsCommandLv2(Channel cserv, Server srv, MapleClient c, String[] sub) { //JrGM - MapleCharacter player = c.getPlayer(); - MapleCharacter victim; - Skill skill; - - switch(sub[0]) { + break; + case "hide": SkillFactory.getSkill(9101004).getEffect(SkillFactory.getSkill(9101004).getMaxLevel()).applyTo(player); break; diff --git a/src/constants/GameConstants.java b/src/constants/GameConstants.java index 2f4269c4c7..5899106a36 100644 --- a/src/constants/GameConstants.java +++ b/src/constants/GameConstants.java @@ -62,26 +62,36 @@ public class GameConstants { 330000, 340000, 350000, 360000, 370000, 380000, 390000, 400000, 410000, 420000, 430000, 440000, 450000, 460000, 470000, 480000, 490000, 500000, 510000, 520000, 530000, 550000, 570000, 590000, 610000, 630000, 650000, 670000, 690000, 710000, 730000, 750000, 770000, 790000, 810000, 830000, 850000, 870000, 890000, 910000}; - public static int getJobMaxLevel(MapleJob job) { - if(job.getId() % 1000 == 0) { // beginner - return 10; - - } else if(job.getId() % 100 == 0) { // 1st job - return 30; - + public static int getJobBranch(MapleJob job) { + int jobid = job.getId(); + + if(jobid % 1000 == 0) { + return 0; + } else if(jobid % 100 == 0) { + return 1; } else { - int jobBranch = job.getId() % 10; - - switch(jobBranch) { - case 0: - return 70; // 2nd job - - case 1: - return 120; // 3rd job - - default: - return (job.getId() / 1000 == 1) ? 120 : 200; // 4th job: cygnus is 120, rest is 200 - } + return 2 + (jobid % 10); + } + } + + public static int getJobMaxLevel(MapleJob job) { + int jobBranch = getJobBranch(job); + + switch(jobBranch) { + case 0: + return 10; // beginner + + case 1: + return 30; // 1st job + + case 2: + return 70; // 2nd job + + case 3: + return 120; // 3rd job + + default: + return (job.getId() / 1000 == 1) ? 120 : 200; // 4th job: cygnus is 120, rest is 200 } } @@ -225,4 +235,17 @@ public class GameConstants { } return mobHpVal[level]; } + + public static String ordinal(int i) { + String[] sufixes = new String[] { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; + switch (i % 100) { + case 11: + case 12: + case 13: + return i + "th"; + + default: + return i + sufixes[i % 10]; + } + } } diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index d0efb470a5..8e466a79c5 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -73,6 +73,10 @@ public class ServerConstants { public static final boolean USE_QUEST_RATE = false; //Exp/Meso gained by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates. public static final boolean USE_MULTIPLE_SAME_EQUIP_DROP = true;//Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data. + + //Announcement Configuration + public static final boolean USE_ANNOUNCE_CHANGEJOB = true; //Automatic message sent to acquantainces when changing jobs. + //Server Rates And Experience public static final int EXP_RATE = 10; public static final int MESO_RATE = 10; @@ -158,7 +162,7 @@ public class ServerConstants { public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo? public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack - + //Event End Timestamp public static final long EVENT_END_TIMESTAMP = 1428897600000L; diff --git a/src/net/server/channel/handlers/GeneralChatHandler.java b/src/net/server/channel/handlers/GeneralChatHandler.java index e304e2dae4..8583404248 100644 --- a/src/net/server/channel/handlers/GeneralChatHandler.java +++ b/src/net/server/channel/handlers/GeneralChatHandler.java @@ -32,6 +32,14 @@ import java.text.SimpleDateFormat; import java.util.Calendar; public final class GeneralChatHandler extends net.AbstractMaplePacketHandler { + private static boolean isCommandIssue(char heading, MapleCharacter chr) { + if(chr.gmLevel() > 1 && heading == '!') { + return true; + } else { + return heading == '@'; + } + } + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { String s = slea.readMapleAsciiString(); @@ -47,7 +55,7 @@ public final class GeneralChatHandler extends net.AbstractMaplePacketHandler { return; } char heading = s.charAt(0); - if (heading == '!' || heading == '@') { + if (isCommandIssue(heading, chr)) { String[] sp = s.split(" "); sp[0] = sp[0].toLowerCase().substring(1); diff --git a/src/net/server/channel/handlers/QuestActionHandler.java b/src/net/server/channel/handlers/QuestActionHandler.java index 69eb70f4d0..f57490edba 100644 --- a/src/net/server/channel/handlers/QuestActionHandler.java +++ b/src/net/server/channel/handlers/QuestActionHandler.java @@ -44,15 +44,21 @@ public final class QuestActionHandler extends AbstractMaplePacketHandler { if (slea.available() >= 4) { slea.readInt(); } - quest.start(player, npc); + + if(quest.canStart(player, npc)) { + quest.start(player, npc); + } } else if (action == 2) { // Complete Quest int npc = slea.readInt(); slea.readInt(); - if (slea.available() >= 2) { - int selection = slea.readShort(); - quest.complete(player, npc, selection); - } else { - quest.complete(player, npc); + + if(quest.canComplete(player, npc)) { + if (slea.available() >= 2) { + int selection = slea.readShort(); + quest.complete(player, npc, selection); + } else { + quest.complete(player, npc); + } } } else if (action == 3) {// forfeit quest quest.forfeit(player); diff --git a/src/scripting/event/EventManager.java b/src/scripting/event/EventManager.java index cd7a4be21d..def406db73 100644 --- a/src/scripting/event/EventManager.java +++ b/src/scripting/event/EventManager.java @@ -34,6 +34,7 @@ import javax.script.Invocable; import javax.script.ScriptException; import constants.ServerConstants; +import constants.GameConstants; import client.MapleCharacter; import net.server.Server; import net.server.world.World; @@ -499,19 +500,6 @@ public class EventManager { return(MapleLifeFactory.getMonster(mid)); } - private static String ordinal(int i) { - String[] sufixes = new String[] { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; - switch (i % 100) { - case 11: - case 12: - case 13: - return i + "th"; - - default: - return i + sufixes[i % 10]; - } - } - private void exportReadyGuild(Integer guildId) { MapleGuild mg = server.getGuild(guildId); String callout = "[Guild Quest] Your guild has been registered to attend to the Sharenian Guild Quest at channel " + this.getChannelServer().getId() @@ -524,7 +512,7 @@ public class EventManager { private void exportMovedQueueToGuild(Integer guildId, int place) { MapleGuild mg = server.getGuild(guildId); String callout = "[Guild Quest] Your guild has been registered to attend to the Sharenian Guild Quest at channel " + this.getChannelServer().getId() - + " and is currently on the " + ordinal(place) + " place on the waiting queue."; + + " and is currently on the " + GameConstants.ordinal(place) + " place on the waiting queue."; mg.dropMessage(6, callout); } diff --git a/src/scripting/quest/QuestScriptManager.java b/src/scripting/quest/QuestScriptManager.java index 22782c71d0..5f647213c5 100644 --- a/src/scripting/quest/QuestScriptManager.java +++ b/src/scripting/quest/QuestScriptManager.java @@ -61,7 +61,7 @@ public class QuestScriptManager extends AbstractScriptManager { qms.put(c, qm); Invocable iv = getInvocable("quest/" + questid + ".js", c); if (iv == null) { - FilePrinter.printError(FilePrinter.QUEST_UNCODED, "Quest " + questid + " is uncoded.\r\n"); + FilePrinter.printError(FilePrinter.QUEST_UNCODED, "START Quest " + questid + " is uncoded.\r\n"); } if (iv == null || QuestScriptManager.getInstance() == null) { qm.dispose(); @@ -112,6 +112,7 @@ public class QuestScriptManager extends AbstractScriptManager { qms.put(c, qm); Invocable iv = getInvocable("quest/" + questid + ".js", c); if (iv == null) { + FilePrinter.printError(FilePrinter.QUEST_UNCODED, "END Quest " + questid + " is uncoded.\r\n"); qm.dispose(); return; } diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index d07e56e318..15d67082eb 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -535,8 +535,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { } }, getAnimationTime("die1")); } - } - else { // is this even necessary? + } else { // is this even necessary? System.out.println("[CRITICAL LOSS] toSpawn is null for " + this.getName()); } diff --git a/tools/MapleCashDropFetcher/build.xml b/tools/MapleCashDropFetcher/build.xml new file mode 100644 index 0000000000..a34b8b10a7 --- /dev/null +++ b/tools/MapleCashDropFetcher/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project MapleCashDropFetcher. + + + diff --git a/tools/MapleCashDropFetcher/lib/CashDropReport.txt b/tools/MapleCashDropFetcher/lib/CashDropReport.txt new file mode 100644 index 0000000000..b60c817722 --- /dev/null +++ b/tools/MapleCashDropFetcher/lib/CashDropReport.txt @@ -0,0 +1,11 @@ + # Report File autogenerated from the MapleCashDropFetcher feature by Ronan Lana. + # Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls. + +NX DROPS ON drop_data +5240005 : 2100100 +5490000 : 9410066 +5490001 : 9410066 + + + + diff --git a/tools/MapleCashDropFetcher/manifest.mf b/tools/MapleCashDropFetcher/manifest.mf new file mode 100644 index 0000000000..328e8e5bc3 --- /dev/null +++ b/tools/MapleCashDropFetcher/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/tools/MapleCashDropFetcher/nbproject/build-impl.xml b/tools/MapleCashDropFetcher/nbproject/build-impl.xml new file mode 100644 index 0000000000..a6ae1f8964 --- /dev/null +++ b/tools/MapleCashDropFetcher/nbproject/build-impl.xml @@ -0,0 +1,1448 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set platform.home + Must set platform.bootcp + Must set platform.java + Must set platform.javac + + The J2SE Platform is not correctly set up. + Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. + Either open the project in the IDE and setup the Platform with the same name or add it manually. + For example like this: + ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) + or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + ${platform.java} -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/MapleCashDropFetcher/nbproject/genfiles.properties b/tools/MapleCashDropFetcher/nbproject/genfiles.properties new file mode 100644 index 0000000000..f63836edd2 --- /dev/null +++ b/tools/MapleCashDropFetcher/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=928d5bb0 +build.xml.script.CRC32=27a7b03c +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=928d5bb0 +nbproject/build-impl.xml.script.CRC32=34e6e8ba +nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 diff --git a/tools/MapleCashDropFetcher/nbproject/project.properties b/tools/MapleCashDropFetcher/nbproject/project.properties new file mode 100644 index 0000000000..a6bd84f5da --- /dev/null +++ b/tools/MapleCashDropFetcher/nbproject/project.properties @@ -0,0 +1,77 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=MapleCashDropFetcher +application.vendor=USER +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Os arquivos em build.classes.dir que devem ser exclu\u00eddos do jar de distribui\u00e7\u00e3o +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/MapleCashDropFetcher.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.mysql-connector-java-bin.jar=../../cores/mysql-connector-java-bin.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.mysql-connector-java-bin.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=maplecashdropfetcher.MapleCashDropFetcher +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=JDK_1.7 +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/tools/MapleCashDropFetcher/nbproject/project.xml b/tools/MapleCashDropFetcher/nbproject/project.xml new file mode 100644 index 0000000000..ea33fc5939 --- /dev/null +++ b/tools/MapleCashDropFetcher/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + MapleCashDropFetcher + + + + + + + + + + diff --git a/tools/MapleCashDropFetcher/src/maplecashdropfetcher/MapleCashDropFetcher.java b/tools/MapleCashDropFetcher/src/maplecashdropfetcher/MapleCashDropFetcher.java new file mode 100644 index 0000000000..3c9509f89d --- /dev/null +++ b/tools/MapleCashDropFetcher/src/maplecashdropfetcher/MapleCashDropFetcher.java @@ -0,0 +1,406 @@ +/* + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package maplecashdropfetcher; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.HashSet; +import java.util.Set; + +import java.io.File; + +import tools.Pair; + +/** + * + * @author RonanLana + + This application gets info from the WZ.XML files regarding cash itemids then searches the drop data on the DB + after any NX (cash item) drops and reports them. + + Estimated parse time: 2 minutes + */ +public class MapleCashDropFetcher { + static String host = "jdbc:mysql://localhost:3306/heavenms"; + static String driver = "com.mysql.jdbc.Driver"; + static String username = "root"; + static String password = ""; + + static String wzPath = "../../wz"; + + static String directoryName = "../.."; + static String newFile = "lib/CashDropReport.txt"; + + static Connection con = null; + static PrintWriter printWriter = null; + static InputStreamReader fileReader = null; + static BufferedReader bufferedReader = null; + + static int initialLength = 200; + static int initialStringLength = 50; + static int itemFileNameSize = 13; + + static Set nxItems = new HashSet<>(); + static Set nxDrops = new HashSet<>(); + + static byte status = 0; + static int currentItemid = 0; + + private static String getName(String token) { + int i, j; + char[] dest; + String d; + + i = token.lastIndexOf("name"); + i = token.indexOf("\"", i) + 1; //lower bound of the string + j = token.indexOf("\"", i); //upper bound + + if(j < i) return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway + + dest = new char[initialStringLength]; + token.getChars(i, j, dest, 0); + + d = new String(dest); + return(d.trim()); + } + + private static String getValue(String token) { + int i, j; + char[] dest; + String d; + + i = token.lastIndexOf("value"); + i = token.indexOf("\"", i) + 1; //lower bound of the string + j = token.indexOf("\"", i); //upper bound + + dest = new char[initialStringLength]; + token.getChars(i, j, dest, 0); + + d = new String(dest); + return(d.trim()); + } + + private static void forwardCursor(int st) { + String line = null; + + try { + while(status >= st && (line = bufferedReader.readLine()) != null) { + simpleToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void simpleToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + } + } + + + private static void inspectEquipWzEntry() { + String line = null; + + try { + while((line = bufferedReader.readLine()) != null) { + translateEquipToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void translateEquipToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + if(status == 1) { + if(!getName(token).equals("info")) { + forwardCursor(status); + } + } + + status += 1; + } + else { + if(status == 2) { + String d = getName(token); + + if(d.equals("cash")) { + if(!getValue(token).equals("0")) { + nxItems.add(currentItemid); + } + + forwardCursor(status); + } + } + } + } + + private static void inspectItemWzEntry() { + String line = null; + + try { + while((line = bufferedReader.readLine()) != null) { + translateItemToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void translateItemToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + if(status == 1) { + currentItemid = Integer.valueOf(getName(token)); + } + else if(status == 2) { + if(!getName(token).equals("info")) { + forwardCursor(status); + } + } + + status += 1; + } + else { + if(status == 3) { + String d = getName(token); + + if(d.equals("cash")) { + if(!getValue(token).equals("0")) { + nxItems.add(currentItemid); + } + + forwardCursor(status); + } + } + } + } + + private static void printReportFileHeader() { + printWriter.println(" # Report File autogenerated from the MapleCashDropFetcher feature by Ronan Lana."); + printWriter.println(" # Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls."); + printWriter.println(); + } + + private static void listFiles(String directoryName, ArrayList files) { + File directory = new File(directoryName); + + // get all the files from a directory + File[] fList = directory.listFiles(); + for (File file : fList) { + if (file.isFile()) { + files.add(file); + } else if (file.isDirectory()) { + listFiles(file.getAbsolutePath(), files); + } + } + } + + private static int getItemIdFromFilename(String name) { + try { + return Integer.valueOf(name.substring(0, name.indexOf('.'))); + } catch(Exception e) { + return -1; + } + } + + private static String getDropTableName(boolean dropdata) { + return (dropdata ? "drop_data" : "reactordrops"); + } + + private static String getDropElementName(boolean dropdata) { + return (dropdata ? "dropperid" : "reactorid"); + } + + private static void filterNxDropsOnDB(boolean dropdata) throws SQLException { + nxDrops.clear(); + + PreparedStatement ps = con.prepareStatement("SELECT DISTINCT itemid FROM " + getDropTableName(dropdata)); + ResultSet rs = ps.executeQuery(); + + while(rs.next()) { + int itemid = rs.getInt("itemid"); + + if(nxItems.contains(itemid)) { + nxDrops.add(itemid); + } + } + + rs.close(); + ps.close(); + } + + private static List> getNxDropsEntries(boolean dropdata) throws SQLException { + List> entries = new ArrayList<>(); + + List sortedNxDrops = new ArrayList<>(nxDrops); + Collections.sort(sortedNxDrops); + + for(Integer nx : sortedNxDrops) { + PreparedStatement ps = con.prepareStatement("SELECT " + getDropElementName(dropdata) + " FROM " + getDropTableName(dropdata) + " WHERE itemid = ?"); + ps.setInt(1, nx); + + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + entries.add(new Pair<>(nx, rs.getInt(getDropElementName(dropdata)))); + } + + rs.close(); + ps.close(); + } + + return entries; + } + + private static void reportNxDropResults(boolean dropdata) throws SQLException { + filterNxDropsOnDB(dropdata); + + if(!nxDrops.isEmpty()) { + List> nxEntries = getNxDropsEntries(dropdata); + + printWriter.println("NX DROPS ON " + getDropTableName(dropdata)); + for(Pair nx : nxEntries) { + printWriter.println(nx.left + " : " + nx.right); + } + printWriter.println("\n\n\n"); + } + } + + private static void ReportNxDropData() { + try { + Class.forName(driver).newInstance(); + + System.out.println("Reading Character.wz ..."); + ArrayList files = new ArrayList<>(); + listFiles(wzPath + "/Character.wz", files); + + for(File f : files) { + //System.out.println("Parsing " + f.getAbsolutePath()); + int itemid = getItemIdFromFilename(f.getName()); + if(itemid < 0) { + continue; + } + + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + currentItemid = itemid; + inspectEquipWzEntry(); + + bufferedReader.close(); + fileReader.close(); + } + + System.out.println("Reading Item.wz ..."); + files = new ArrayList<>(); + listFiles(wzPath + "/Item.wz", files); + + for(File f : files) { + //System.out.println("Parsing " + f.getAbsolutePath()); + fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + if(f.getName().length() <= itemFileNameSize) { + inspectItemWzEntry(); + } else { // pet file structure is similar to equips, maybe there are other item-types following this behaviour? + int itemid = getItemIdFromFilename(f.getName()); + if(itemid < 0) { + continue; + } + + currentItemid = itemid; + inspectEquipWzEntry(); + } + + bufferedReader.close(); + fileReader.close(); + } + + System.out.println("Reporting results..."); + + // filter drop data on DB + con = DriverManager.getConnection(host, username, password); + + // report suspects of missing quest drop data, as well as those drop data that may have incorrect questids. + printWriter = new PrintWriter(newFile, "UTF-8"); + printReportFileHeader(); + + reportNxDropResults(true); + reportNxDropResults(false); + + /* + printWriter.println("NX LIST"); // list of all cash items found + for(Integer nx : nxItems) { + printWriter.println(nx); + } + */ + + con.close(); + printWriter.close(); + System.out.println("Done!"); + } + + catch(SQLException e) { + System.out.println("Warning: Could not establish connection to database to report quest data."); + System.out.println(e.getMessage()); + } + + catch(ClassNotFoundException e) { + System.out.println("Error: could not find class"); + System.out.println(e.getMessage()); + } + + catch(InstantiationException e) { + System.out.println("Error: instantiation failure"); + System.out.println(e.getMessage()); + } + + catch(Exception e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + ReportNxDropData(); + } + +} diff --git a/tools/MapleCashDropFetcher/src/tools/DatabaseConnection.java b/tools/MapleCashDropFetcher/src/tools/DatabaseConnection.java new file mode 100644 index 0000000000..9dcd4e6545 --- /dev/null +++ b/tools/MapleCashDropFetcher/src/tools/DatabaseConnection.java @@ -0,0 +1,51 @@ +package tools; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * @author Frz (Big Daddy) + * @author The Real Spookster (some modifications to this beautiful code) + */ +public class DatabaseConnection { + private static String DB_URL = "jdbc:mysql://localhost:3306/heavenms"; + private static String DB_USER = "root"; + private static String DB_PASS = ""; + + public static final int RETURN_GENERATED_KEYS = 1; + + private static ThreadLocal con = new ThreadLocalConnection(); + + public static Connection getConnection() { + Connection c = con.get(); + try { + c.getMetaData(); + } catch (SQLException e) { // connection is dead, therefore discard old object 5ever + con.remove(); + c = con.get(); + } + return c; + } + + private static class ThreadLocalConnection extends ThreadLocal { + + @Override + protected Connection initialValue() { + try { + Class.forName("com.mysql.jdbc.Driver"); // touch the mysql driver + } catch (ClassNotFoundException e) { + System.out.println("[SEVERE] SQL Driver Not Found. Consider death by clams."); + e.printStackTrace(); + return null; + } + try { + return DriverManager.getConnection(DB_URL, DB_USER, DB_PASS); + } catch (SQLException e) { + System.out.println("[SEVERE] Unable to make database connection."); + e.printStackTrace(); + return null; + } + } + } +} \ No newline at end of file diff --git a/tools/MapleCashDropFetcher/src/tools/Pair.java b/tools/MapleCashDropFetcher/src/tools/Pair.java new file mode 100644 index 0000000000..f88718cbe3 --- /dev/null +++ b/tools/MapleCashDropFetcher/src/tools/Pair.java @@ -0,0 +1,121 @@ +/* +This file is part of the OdinMS Maple Story Server +Copyright (C) 2008 ~ 2010 Patrick Huy +Matthias Butz +Jan Christian Meyer + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License version 3 +as published by the Free Software Foundation. You may not use, modify +or distribute this program under any other version of the +GNU Affero General Public License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + */ +package tools; + +/** + * Represents a pair of values. + * + * @author Frz + * @since Revision 333 + * @version 1.0 + * + * @param The type of the left value. + * @param The type of the right value. + */ +public class Pair { + + public E left; + public F right; + + /** + * Class constructor - pairs two objects together. + * + * @param left The left object. + * @param right The right object. + */ + public Pair(E left, F right) { + this.left = left; + this.right = right; + } + + /** + * Gets the left value. + * + * @return The left value. + */ + public E getLeft() { + return left; + } + + /** + * Gets the right value. + * + * @return The right value. + */ + public F getRight() { + return right; + } + + /** + * Turns the pair into a string. + * + * @return Each value of the pair as a string joined by a colon. + */ + @Override + public String toString() { + return left.toString() + ":" + right.toString(); + } + + /** + * Gets the hash code of this pair. + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((left == null) ? 0 : left.hashCode()); + result = prime * result + ((right == null) ? 0 : right.hashCode()); + return result; + } + + /** + * Checks to see if two pairs are equal. + */ + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Pair other = (Pair) obj; + if (left == null) { + if (other.left != null) { + return false; + } + } else if (!left.equals(other.left)) { + return false; + } + if (right == null) { + if (other.right != null) { + return false; + } + } else if (!right.equals(other.right)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/tools/MapleMesoFetcher/nbproject/private/private.xml b/tools/MapleMesoFetcher/nbproject/private/private.xml index f02aee0624..6807a2ba19 100644 --- a/tools/MapleMesoFetcher/nbproject/private/private.xml +++ b/tools/MapleMesoFetcher/nbproject/private/private.xml @@ -2,8 +2,6 @@ - - file:/C:/Nexon/MapleSolaxia/MapleSolaxiaV2/tools/MapleMesoFetcher/src/maplemesofetcher/MapleMesoFetcher.java - + diff --git a/tools/MapleQuestItemFetcher/dist/MapleQuestItemFetcher.jar b/tools/MapleQuestItemFetcher/dist/MapleQuestItemFetcher.jar index eecb39f117..a81e6a6878 100644 Binary files a/tools/MapleQuestItemFetcher/dist/MapleQuestItemFetcher.jar and b/tools/MapleQuestItemFetcher/dist/MapleQuestItemFetcher.jar differ diff --git a/tools/MapleQuestItemFetcher/lib/QuestReport.txt b/tools/MapleQuestItemFetcher/lib/QuestReport.txt index 23c39d930f..0d20e38d85 100644 --- a/tools/MapleQuestItemFetcher/lib/QuestReport.txt +++ b/tools/MapleQuestItemFetcher/lib/QuestReport.txt @@ -2,33 +2,28 @@ # Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls. INCORRECT QUESTIDS ON DB -4001342 : 7777 -> 28175 +2022055 : -1 -> 9330 EXPIRED +2022056 : -1 -> 9330 EXPIRED +4001352 : 28205 -> 28206 +4001366 : 28194 -> 28195 4001367 : 28257 -> 28262 4001368 : 28258 -> 28262 4001369 : 28259 -> 28262 4001370 : 28260 -> 28262 4001371 : 28261 -> 28262 +4001372 : 28344 -> 28282 +4031107 : -1 -> 3409 +4031116 : -1 -> 3419 4031130 : 0 -> 3238 4031164 : 0 -> 2084 -4031171 : 0 -> 7101 -4031172 : 7103 -> 7106 4031189 : 0 -> 3448 4031218 : 0 -> 3071 4031223 : 3607 -> 3608 -4031343 : 6904 -> 6905 -4031344 : 6904 -> 6905 -4031405 : 0 -> 4207 +4031405 : 0 -> 4207 EXPIRED 4031511 : 6904 -> 6914 -4031512 : 6914 -> 6915 -4031514 : 6924 -> 6925 -4031515 : 6924 -> 6925 -4031517 : 6934 -> 6935 -4031518 : 6934 -> 6935 4031856 : 0 -> 2191 4031857 : 0 -> 2192 -4031860 : 6944 -> 6945 -4031861 : 6944 -> 6945 -4031871 : 6350 -> 28344 +4032319 : -1 -> 21723 4032324 : 21736 -> 21737 4032339 : 0 -> 21303 @@ -38,283 +33,195 @@ INCORRECT QUESTIDS ON DB ITEMS WITH NO QUEST DROP DATA ON DB -1002436 - 2075 -1102057 - 7103 -1102061 - 3066 1302014 - 2048 -2022053 - 9330 -2022054 - 9330 -2022055 - 9330 -2022056 - 9330 -2022057 - 9332 -2022281 - 8569 -2100016 - 3223 -2100017 - 3419 -2100018 - 3236 -2100019 - 3238 -3994139 - 10360 -4001118 - 3814 -4001340 - 28167 -4001347 - 28229 -4001348 - 28231 -4001349 - 28235 -4001350 - 28235 -4001351 - 28237 -4001352 - 28206 +2022053 - 9330 EXPIRED +2022054 - 9330 EXPIRED +2022057 - 9332 EXPIRED +3994139 - 10360 EXPIRED +4000142 - 1018 4001353 - 28227 -4001366 - 28195 4031014 - 2020 -4031015 - 2022 -4031019 - 9411 +4031015 - 2020 4031020 - 2050 +4031025 - 2052 +4031026 - 2053 +4031028 - 2054 4031032 - 2051 4031039 - 2055 4031040 - 2056 4031041 - 2057 4031042 - 2035 -4031063 - 9260 +4031063 - 9260 EXPIRED 4031064 - 8012 -4031107 - 3409 -4031116 - 3419 4031117 - 3421 -4031122 - 9340 -4031124 - 9340 -4031134 - 3443 -4031136 - 3439 -4031141 - 3407 -4031142 - 3407 -4031143 - 3407 +4031122 - 9340 EXPIRED +4031124 - 9340 EXPIRED 4031144 - 2047 -4031150 - 2067 -4031157 - 2074 -4031158 - 2074 -4031165 - 2086 -4031167 - 9052 -4031168 - 9055 -4031169 - 9058 +4031167 - 9052 EXPIRED +4031168 - 9055 EXPIRED +4031169 - 9058 EXPIRED 4031180 - 8020 -4031181 - 9140 -4031182 - 9140 -4031183 - 9140 -4031184 - 9150 -4031185 - 9150 -4031186 - 9150 -4031190 - 3054 +4031181 - 9140 EXPIRED +4031182 - 9140 EXPIRED +4031183 - 9140 EXPIRED +4031184 - 9150 EXPIRED +4031185 - 9150 EXPIRED +4031186 - 9150 EXPIRED +4031190 - 3055 4031191 - 3063 -4031192 - 8700 -4031198 - 3043 -4031199 - 3046 -4031200 - 3069 +4031192 - 8700 EXPIRED +4031199 - 3045 4031201 - 3048 4031202 - 3050 4031207 - 3443 -4031220 - 9210 -4031225 - 3606 -4031226 - 9321 +4031220 - 9210 EXPIRED +4031226 - 9321 EXPIRED 4031227 - 4103 EXPIRED 4031230 - 3619 -4031231 - 3620 -4031235 - 3607 +4031235 - 3615 4031236 - 3616 -4031237 - 3605 -4031238 - 3611 +4031237 - 3617 +4031238 - 3618 4031243 - 3443 -4031257 - 9350 -4031258 - 9351 -4031270 - 3629 -4031271 - 9351 -4031272 - 9352 -4031274 - 3083 -4031275 - 3083 -4031276 - 3083 -4031277 - 3083 -4031278 - 3083 -4031280 - 3632 -4031290 - 4106 -4031291 - 4006 -4031292 - 4009 -4031293 - 4010 -4031296 - 4010 -4031297 - 9386 -4031298 - 3636 -4031301 - 9391 -4031302 - 9503 -4031303 - 4008 -4031304 - 9392 -4031321 - 9504 -4031352 - 4005 -4031354 - 4013 -4031388 - 4218 -4031418 - 8823 -4031419 - 8823 -4031420 - 8823 -4031421 - 8823 -4031425 - 8822 +4031257 - 9350 EXPIRED +4031270 - 3630 +4031271 - 9351 EXPIRED +4031272 - 9352 EXPIRED +4031280 - 3633 +4031290 - 4104 EXPIRED +4031291 - 4006 EXPIRED +4031292 - 4006 EXPIRED +4031293 - 4006 EXPIRED +4031296 - 4010 EXPIRED +4031297 - 9386 EXPIRED +4031298 - 3639 +4031301 - 9391 EXPIRED +4031302 - 9503 EXPIRED +4031303 - 4007 EXPIRED +4031304 - 9392 EXPIRED +4031321 - 9504 EXPIRED +4031352 - 4005 EXPIRED +4031354 - 4013 EXPIRED +4031388 - 4218 EXPIRED +4031418 - 8823 EXPIRED +4031419 - 8823 EXPIRED +4031420 - 8823 EXPIRED +4031421 - 8823 EXPIRED 4031448 - 6134 -4031450 - 6263 -4031452 - 6201 -4031454 - 6281 4031455 - 6280 4031456 - 6230 4031462 - 6210 4031468 - 6222 -4031471 - 6153 4031478 - 6210 4031488 - 6312 -4031495 - 6192 -4031504 - 9640 -4031505 - 9641 -4031506 - 9642 -4031507 - 6002 -4031508 - 6002 +4031504 - 9640 EXPIRED +4031505 - 9641 EXPIRED +4031506 - 9642 EXPIRED 4031554 - 3821 -4031557 - 9710 -4031558 - 9711 -4031559 - 9712 -4031560 - 9713 -4031561 - 9714 -4031563 - 8850 -4031564 - 8851 -4031565 - 8852 -4031566 - 8853 -4031567 - 8854 -4031568 - 3911 -4031570 - 3939 -4031571 - 3941 -4031574 - 3935 +4031557 - 9710 EXPIRED +4031558 - 9711 EXPIRED +4031559 - 9712 EXPIRED +4031560 - 9713 EXPIRED +4031561 - 9714 EXPIRED +4031563 - 8855 +4031564 - 8856 +4031565 - 8857 +4031566 - 8858 +4031567 - 8859 +4031570 - 3938 +4031571 - 3940 4031578 - 3923 -4031581 - 3937 -4031582 - 3901 -4031584 - 9731 -4031585 - 9732 -4031586 - 9740 -4031587 - 9741 -4031588 - 9742 -4031590 - 8881 -4031608 - 9803 -4031611 - 9804 -4031612 - 9805 +4031582 - 3949 +4031584 - 9731 EXPIRED +4031585 - 9732 EXPIRED +4031586 - 9740 EXPIRED +4031587 - 9741 EXPIRED +4031588 - 9742 EXPIRED +4031590 - 8881 EXPIRED +4031608 - 9803 EXPIRED +4031611 - 9804 EXPIRED +4031612 - 9805 EXPIRED 4031625 - 9820 -4031661 - 9861 -4031662 - 9866 -4031667 - 9863 -4031683 - 1115 -4031684 - 1116 -4031685 - 1117 -4031686 - 1118 -4031687 - 1119 -4031688 - 1120 -4031689 - 1121 -4031690 - 1122 -4031691 - 1123 -4031692 - 1124 -4031695 - 3335 +4031661 - 9861 EXPIRED +4031662 - 9866 EXPIRED +4031667 - 9863 EXPIRED 4031696 - 3334 4031697 - 3322 -4031703 - 3302 4031708 - 3309 -4031709 - 3310 -4031737 - 3343 -4031764 - 4949 -4031766 - 4959 -4031767 - 4947 -4031768 - 4953 -4031769 - 4946 -4031770 - 4946 -4031771 - 4944 +4031764 - 4949 EXPIRED +4031766 - 4959 EXPIRED +4031767 - 4947 EXPIRED +4031769 - 4946 EXPIRED +4031770 - 4946 EXPIRED 4031772 - 4942 4031774 - 3361 4031785 - 3376 -4031789 - 3844 4031796 - 3362 4031797 - 3367 -4031798 - 3366 -4031801 - 1040 -4031806 - 3380 -4031812 - 4950 -4031833 - 9946 -4031837 - 9945 +4031806 - 3379 +4031812 - 4950 EXPIRED +4031837 - 9942 EXPIRED 4031839 - 2162 -4031881 - 4484 -4031894 - 2214 +4031850 - 2180 +4031881 - 4484 EXPIRED 4031921 - 4646 -4031927 - 3454 -4031928 - 3454 -4031945 - 9987 -4032037 - 9154 -4032038 - 9154 -4032039 - 9154 -4032055 - 4675 -4032087 - 10081 -4032092 - 28003 -4032119 - 28109 +4032037 - 9154 EXPIRED +4032038 - 9154 EXPIRED +4032039 - 9154 EXPIRED +4032087 - 10081 EXPIRED +4032119 - 28109 EXPIRED 4032136 - 20710 -4032138 - 20713 +4032138 - 20712 4032142 - 20716 -4032143 - 20717 4032196 - 20528 4032197 - 20528 4032198 - 20528 -4032233 - 8298 -4032234 - 8299 -4032235 - 8299 -4032236 - 8299 -4032237 - 8299 -4032238 - 8299 -4032239 - 8299 -4032247 - 28103 -4032248 - 28108 -4032264 - 10240 -4032265 - 10241 -4032266 - 10240 -4032270 - 10241 -4032271 - 10260 -4032272 - 10268 -4032273 - 10268 -4032275 - 10261 -4032276 - 10262 -4032277 - 10263 -4032278 - 10270 -4032279 - 10271 -4032280 - 10272 -4032281 - 10270 -4032282 - 10271 -4032283 - 10272 -4032284 - 10264 -4032285 - 10265 -4032286 - 10266 -4032287 - 10267 -4032307 - 28121 -4032308 - 28122 -4032317 - 21717 -4032318 - 21718 -4032319 - 21723 -4032321 - 21727 -4032325 - 21752 +4032233 - 8298 EXPIRED +4032234 - 8299 EXPIRED +4032235 - 8299 EXPIRED +4032236 - 8299 EXPIRED +4032237 - 8299 EXPIRED +4032238 - 8299 EXPIRED +4032239 - 8298 EXPIRED +4032248 - 28108 EXPIRED +4032264 - 10240 EXPIRED +4032265 - 10241 EXPIRED +4032266 - 10240 EXPIRED +4032270 - 10241 EXPIRED +4032271 - 10260 EXPIRED +4032272 - 10268 EXPIRED +4032273 - 10268 EXPIRED +4032275 - 10261 EXPIRED +4032276 - 10262 EXPIRED +4032277 - 10263 EXPIRED +4032278 - 10270 EXPIRED +4032279 - 10271 EXPIRED +4032280 - 10272 EXPIRED +4032281 - 10270 EXPIRED +4032282 - 10271 EXPIRED +4032283 - 10272 EXPIRED +4032284 - 10264 EXPIRED +4032285 - 10265 EXPIRED +4032286 - 10266 EXPIRED +4032287 - 10267 EXPIRED +4032306 - 28120 EXPIRED +4032307 - 28121 EXPIRED +4032308 - 28122 EXPIRED +4032325 - 21751 4032326 - 21752 4032331 - 21601 4032333 - 21608 -4032335 - 21617 -4032342 - 21743 -4032348 - 10300 -4032349 - 10301 -4032350 - 10302 -4032374 - 2405 -4032376 - 2406 -4032377 - 2407 -4032378 - 2408 -4032379 - 2409 +4032348 - 10300 EXPIRED +4032349 - 10301 EXPIRED +4032350 - 10302 EXPIRED 4032401 - 2261 4032402 - 2263 -4032404 - 28128 +4032404 - 28128 EXPIRED 4032423 - 21767 -4032435 - 28307 -4032436 - 28314 -4032437 - 28321 -4032443 - 28317 -4032496 - 28238 -4032512 - 3720 -4161000 - 9322 +4032435 - 28307 EXPIRED +4032436 - 28314 EXPIRED +4032437 - 28321 EXPIRED +4161000 - 9322 EXPIRED @@ -322,87 +229,27 @@ ITEMS WITH NO QUEST DROP DATA ON DB COMPLETE QUEST ITEMS WITH ZERO QUANTITY -1018: - 4000142 - -2052: - 4031025 - -2053: - 4031026 - -2054: - 4031028 - -2167: - 4031841 - -2168: - 4031842 - -2169: - 4031843 - -2173: - 4031846 - -2180: - 4031850 - -2183: - 4031851 - -2185: - 4031852 - -3010: - 4031050 - -6340: - 4031872 - -6350: - 4031871 - -6360: - 4031869 - -6361: - 4031870 - -6380: - 4031873 - -6390: - 4031874 - 8142: 4000300 4000301 -8218: +8218 EXPIRED: 4031664 4031665 4031666 -8886: +8886 EXPIRED: 4031659 -8887: +8887 EXPIRED: 4031658 -8888: +8888 EXPIRED: 4031660 -10430: - 4220152 - 28104: 4032247 -28120: - 4032306 - diff --git a/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java b/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java index 8231c38585..e6879b655a 100644 --- a/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java +++ b/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java @@ -72,7 +72,6 @@ public class MapleQuestItemFetcher { static String password = ""; static String wzPath = "../../wz"; - static String fileName = "../../wz/Quest.wz/Act.img.xml"; static String directoryName = "../.."; static String newFile = "lib/QuestReport.txt"; @@ -134,6 +133,28 @@ public class MapleQuestItemFetcher { return(d.trim()); } + private static void forwardCursor(int st) { + String line = null; + + try { + while(status >= st && (line = bufferedReader.readLine()) != null) { + simpleToken(line); + } + } + catch(Exception e) { + e.printStackTrace(); + } + } + + private static void simpleToken(String token) { + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + status += 1; + } + } + private static void inspectQuestItemList(int st) { String line = null; @@ -152,24 +173,28 @@ public class MapleQuestItemFetcher { if(ii.isQuestItem(currentItemid)) { if(currentCount != 0) { if(isCompleteState == 1) { - Set qi = completeQuestItems.get(questId); - if(qi == null) { - Set newSet = new HashSet<>(); - newSet.add(currentItemid); + if(currentCount < 0) { + Set qi = completeQuestItems.get(questId); + if(qi == null) { + Set newSet = new HashSet<>(); + newSet.add(currentItemid); - completeQuestItems.put(questId, newSet); - } else { - qi.add(currentItemid); + completeQuestItems.put(questId, newSet); + } else { + qi.add(currentItemid); + } } } else { - Set qi = startQuestItems.get(questId); - if(qi == null) { - Set newSet = new HashSet<>(); - newSet.add(currentItemid); + if(currentCount > 0) { + Set qi = startQuestItems.get(questId); + if(qi == null) { + Set newSet = new HashSet<>(); + newSet.add(currentItemid); - startQuestItems.put(questId, newSet); - } else { - qi.add(currentItemid); + startQuestItems.put(questId, newSet); + } else { + qi.add(currentItemid); + } } } } else { @@ -222,7 +247,7 @@ public class MapleQuestItemFetcher { } } - private static void translateToken(String token) { + private static void translateActToken(String token) { String d; int temp; @@ -244,6 +269,8 @@ public class MapleQuestItemFetcher { if(d.contains("item")) { temp = status; inspectQuestItemList(temp); + } else { + forwardCursor(status); } } @@ -258,6 +285,37 @@ public class MapleQuestItemFetcher { } } } + + private static void translateCheckToken(String token) { + String d; + + if(token.contains("/imgdir")) { + status -= 1; + } + else if(token.contains("imgdir")) { + if(status == 1) { //getting QuestId + d = getName(token); + questId = Integer.parseInt(d); + } + else if(status == 2) { //start/complete + d = getName(token); + isCompleteState = Integer.parseInt(d); + } + else if(status == 3) { + forwardCursor(status); + } + + status += 1; + } else { + if(status == 3) { + d = getName(token); + + if(d.equals("end")) { + limitedQuestids.add(questId); + } + } + } + } private static void calculateQuestItemDiff() { // This will remove started quest items from the "to complete" item set. @@ -289,32 +347,46 @@ public class MapleQuestItemFetcher { return list; } + private static String getTableName(boolean dropdata) { + return dropdata ? "drop_data" : "reactordrops"; + } + + private static void filterQuestDropsOnTable(Pair iq, List> itemsWithQuest, boolean dropdata) throws SQLException { + PreparedStatement ps = con.prepareStatement("SELECT questid FROM " + getTableName(dropdata) + " WHERE itemid = ?;"); + ps.setInt(1, iq.getLeft()); + ResultSet rs = ps.executeQuery(); + + if (rs.isBeforeFirst()) { + while(rs.next()) { + int curQuest = rs.getInt(1); + if(curQuest != iq.getRight()) { + Set sqSet = startQuestItems.get(curQuest); + if(sqSet != null && sqSet.contains(iq.getLeft())) { + continue; + } + + int[] mixed = new int[3]; + mixed[0] = iq.getLeft(); + mixed[1] = curQuest; + mixed[2] = iq.getRight(); + + mixedQuestidItems.put(iq.getLeft(), mixed); + } + } + + itemsWithQuest.remove(iq); + } + + rs.close(); + ps.close(); + } + private static void filterQuestDropsOnDB(List> itemsWithQuest) throws SQLException { List> copyItemsWithQuest = new ArrayList<>(itemsWithQuest); try { for(Pair iq : copyItemsWithQuest) { - PreparedStatement ps = con.prepareStatement("SELECT questid FROM drop_data WHERE itemid = ?;"); - ps.setInt(1, iq.getLeft()); - ResultSet rs = ps.executeQuery(); - - if (rs.isBeforeFirst()) { - while(rs.next()) { - int curQuest = rs.getInt(1); - if(curQuest != iq.getRight()) { - int[] mixed = new int[3]; - mixed[0] = iq.getLeft(); - mixed[1] = curQuest; - mixed[2] = iq.getRight(); - - mixedQuestidItems.put(iq.getLeft(), mixed); - } - } - - itemsWithQuest.remove(iq); - } - - rs.close(); - ps.close(); + filterQuestDropsOnTable(iq, itemsWithQuest, true); + filterQuestDropsOnTable(iq, itemsWithQuest, false); } } catch(SQLException e) { @@ -431,17 +503,30 @@ public class MapleQuestItemFetcher { private static void ReportQuestItemData() { // This will reference one line at a time String line = null; + String fileName = null; try { Class.forName(driver).newInstance(); System.out.println("Reading WZs..."); - + + fileName = wzPath + "/Quest.wz/Check.img.xml"; fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8"); bufferedReader = new BufferedReader(fileReader); while((line = bufferedReader.readLine()) != null) { - translateToken(line); + translateCheckToken(line); // fetch expired quests through here as well + } + + bufferedReader.close(); + fileReader.close(); + + fileName = wzPath + "/Quest.wz/Act.img.xml"; + fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + + while((line = bufferedReader.readLine()) != null) { + translateActToken(line); } bufferedReader.close(); diff --git a/wz/Quest.wz/Check.img.xml b/wz/Quest.wz/Check.img.xml index 98a6cc7275..149571986a 100644 --- a/wz/Quest.wz/Check.img.xml +++ b/wz/Quest.wz/Check.img.xml @@ -22139,12 +22139,7 @@ - - - - - - + diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 0cc87d7222..1c3100271c 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -4743,510 +4743,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -9204,4 +8700,508 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +