diff --git a/config.yaml b/config.yaml index 35125c96f4..182227bda5 100644 --- a/config.yaml +++ b/config.yaml @@ -193,7 +193,7 @@ server: #Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses. #Multiclient Coordinator Configuration - MAX_ALLOWED_ACCOUNT_HWID: 4 #Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login. + MAX_ALLOWED_ACCOUNT_HWID: 10 #Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login. MAX_ACCOUNT_LOGIN_ATTEMPT: 15 #After N tries on an account, login on that account gets disabled for a short period. LOGIN_ATTEMPT_DURATION: 120 #Period in seconds the login attempt remains registered on the system. diff --git a/docs/issues.txt b/docs/issues.txt index 4cb1acf797..d7125f68ae 100644 --- a/docs/issues.txt +++ b/docs/issues.txt @@ -10,7 +10,7 @@ Known issues: - If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen. - Sometimes battleship may behave oddly with the enhanced buff system, making the character d/c in certain scenarios. - Dragon Roar doesn't show the stun effect to players. -- Cygnus job 'Final Attack' skills not functional. +- Cygnus job 'Final Attack' skill for Wind Archer not functional. - Steal skill doesn't deduct the loot from the drop pool from a mob. - Snipe will show much higher damage value than actually applicable to the attacker. - Some monster status such as weapon/magic reflect doesn't behave properly in certain scenarios. diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 39c603514e..a4e71c40a2 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -5,6 +5,7 @@ Dalair -> 9000040 Donation Box -> 9000041 Abdula -> 9209000 * + MapleTV -> scroll_generator CUSTOM NPC SHOPS (db_shopupdate.sql): Asia -> 2082014 @@ -2272,4 +2273,36 @@ Corrigido caso inesperado em 2nd job de pirata bloqueando saída de jogadores do 13 Novembro 2019, Corrigido problema no sistema de matching ao tentar rodar ações externas enquanto travando os recursos do sistema, ao criar match. Corrigido caso onde novos jogadores poderiam ser agregados à party e entrar em campo na MCPQ assim que confirmação de partida e contagem de início fossem efetivados. -Adicionado cache para requerimento de scrolls, assim melhorando tempo de resposta para o novo custom NPC de geração de scrolls. \ No newline at end of file +Adicionado cache para requerimento de scrolls, assim melhorando tempo de resposta para o novo custom NPC de geração de scrolls. + +15 Novembro 2019, +Reajustado chance de drops de vários livros de quest para skills de 4o job. + +18 Novembro 2019, +Corrigido atividade recente evitando skills em área de mobs não acertando mais diversos jogadores. +Corrigido uso de facingLeft em aplicação de mobskills levando a cálculos inviáveis de área de atuação. +Aprimorado sistema multi-cliente, agora permitindo login de jogadores dentro de uma mesma rede. +Revisado mensagens de expedição finalizando abruptamente aparecendo para jogador que está saindo do evento. +Revisado posicionamento de laranja na loja da NPC Miki. +Corrigido drop de quest de mineiros que deveria aparecer frequentemente dropando muito raramente. +Revisado área restrita de quest para skill Rush não permitindo acesso a vários jogadores simultaneamente. +Corrigido ocorrência de loop contínuo no sistema de matching, para casos com mais de um respondente. +Corrigido checagem por mesos de dono das Player Shops ocorrendo após inserção do produto no inventário. +Corrigido alerta de autopots atuando sempre, uma vez que configurações foram setadas no máximo. Modelo agora passa a usar esfriamento de limites para reaquisição dos limites do lado cliente. + +19 - 20 Novembro 2019, +Revisado abordagem de locks de canal e mundo na classe principal. +Implementado detecção por questbooks pelo anunciador de skillbooks. +Adicionado buffs com bonus na contabilização de dano teto feita no handler de danos aplicados. +Implementado detecção de skills recompensadas por quests além dos itens de skillbooks pelo anunciador. +Corrigido caso onde drops de quest não estavam aparecendo caso fossem únicos na lista de itens a aparecer e last hitter não precisasse dos mesmos. + +22 - 23 Novembro 2019, +Corrigido caso de jogador entrando em estado inconsistente com storage de jogadores ao mudar de mapas. +Corrigido bug no método de checagem de quantidade de slots no inventário para múltiplos do mesmo equipamento. +Corrigido buff Final Attack de Cygnus sendo reaplicado a todo acerto de skill. + +24 - 25 Novembro 2019, +Corrigido caso não sendo checado devidamente com Maker. +Corrigido contagem de projéteis nos stats de skill usando tipo de dados de tamanho insuficiente. +Refatorado acesso a membros relativos a Dojo em canais de forma a buscar melhorar efetividade dos ingressos e liberações de lobby. \ No newline at end of file diff --git a/scripts/event/CWKPQ.js b/scripts/event/CWKPQ.js index 87b5eddc4b..9e790d89fe 100644 --- a/scripts/event/CWKPQ.js +++ b/scripts/event/CWKPQ.js @@ -242,8 +242,8 @@ function scheduledTimeout(eim) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { if (eim.isEventTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { @@ -310,8 +310,8 @@ function playerRevive(eim, player) { function playerDisconnected(eim, player) { if (eim.isEventTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { diff --git a/scripts/event/HorntailBattle.js b/scripts/event/HorntailBattle.js index 433d236241..803d102bb9 100644 --- a/scripts/event/HorntailBattle.js +++ b/scripts/event/HorntailBattle.js @@ -127,8 +127,8 @@ function scheduledTimeout(eim) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { @@ -156,8 +156,8 @@ function playerRevive(eim, player) { function playerDisconnected(eim, player) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { diff --git a/scripts/event/PinkBeanBattle.js b/scripts/event/PinkBeanBattle.js index b29e631378..0f1729087e 100644 --- a/scripts/event/PinkBeanBattle.js +++ b/scripts/event/PinkBeanBattle.js @@ -129,8 +129,8 @@ function scheduledTimeout(eim) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { @@ -170,8 +170,8 @@ function monsterRevive(eim, mob) { function playerDisconnected(eim, player) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { diff --git a/scripts/event/ScargaBattle.js b/scripts/event/ScargaBattle.js index d7d4b9ff1d..5c678722b7 100644 --- a/scripts/event/ScargaBattle.js +++ b/scripts/event/ScargaBattle.js @@ -113,8 +113,8 @@ function scheduledTimeout(eim) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { @@ -142,8 +142,8 @@ function playerRevive(eim, player) { function playerDisconnected(eim, player) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { diff --git a/scripts/event/ShowaBattle.js b/scripts/event/ShowaBattle.js index f3504671a3..fb7087c412 100644 --- a/scripts/event/ShowaBattle.js +++ b/scripts/event/ShowaBattle.js @@ -120,8 +120,8 @@ function scheduledTimeout(eim) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { @@ -151,8 +151,8 @@ function playerRevive(eim, player) { function playerDisconnected(eim, player) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { diff --git a/scripts/event/ZakumBattle.js b/scripts/event/ZakumBattle.js index 63a3bb9276..38df9109c7 100644 --- a/scripts/event/ZakumBattle.js +++ b/scripts/event/ZakumBattle.js @@ -117,8 +117,8 @@ function scheduledTimeout(eim) { function changedMap(eim, player, mapid) { if (mapid < minMapId || mapid > maxMapId) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { @@ -146,8 +146,8 @@ function playerRevive(eim, player) { function playerDisconnected(eim, player) { if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) { - eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); eim.unregisterPlayer(player); + eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it."); end(eim); } else { diff --git a/scripts/map/onFirstUserEnter/dojang_1st.js b/scripts/map/onFirstUserEnter/dojang_1st.js index 3392697404..07c210d9ff 100644 --- a/scripts/map/onFirstUserEnter/dojang_1st.js +++ b/scripts/map/onFirstUserEnter/dojang_1st.js @@ -31,15 +31,12 @@ function start(ms) { var stage = Math.floor(ms.getMapId() / 100) % 100; var callBoss = false; - if (stage % 6 == 1) { - ms.getClient().getChannelServer().startDojoSchedule(ms.getMapId()); - } else if(stage % 6 == 0) { + if (stage % 6 == 0) { ms.getClient().getChannelServer().dismissDojoSchedule(ms.getMapId(), ms.getParty()); - } + ms.getClient().getChannelServer().setDojoProgress(ms.getMapId()); + } else { + callBoss = ms.getClient().getChannelServer().setDojoProgress(ms.getMapId()); - callBoss = ms.getClient().getChannelServer().setDojoProgress(ms.getMapId()); - - if (stage % 6 > 0) { var realstage = stage - ((stage / 6) | 0); var mob = ms.getMonsterLifeFactory(9300183 + realstage); if (callBoss && mob != null && ms.getPlayer().getMap().getMonsterById(9300216) == null) { diff --git a/scripts/npc/2091005.js b/scripts/npc/2091005.js index b49cb7aff8..f83b0fd997 100644 --- a/scripts/npc/2091005.js +++ b/scripts/npc/2091005.js @@ -26,6 +26,7 @@ */ importPackage(Packages.config); +importPackage(Packages.constants.game); var disabled = false; var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004); @@ -69,7 +70,7 @@ function action(mode, type, selection) { if(status == 0) { if (isRestingSpot(cm.getPlayer().getMap().getId())) { var text = "I'm surprised you made it this far! But it won't be easy from here on out. You still want the challenge?\r\n\r\n#b#L0#I want to continue#l\r\n#L1#I want to leave#l\r\n"; - if (!cm.getPlayer().getDojoParty()) { + if (!GameConstants.isDojoPartyArea(cm.getPlayer().getMapId())) { text += "#L2#I want to record my score up to this point#l"; } cm.sendSimple(text); @@ -99,7 +100,7 @@ function action(mode, type, selection) { cm.dispose(); return; } else { - var avDojo = cm.getClient().getChannelServer().getAvailableDojo(true); + var avDojo = cm.getClient().getChannelServer().ingressDojo(true, 0); if(avDojo < 0) { if(avDojo == -1) cm.sendOk("All Dojo's are being used already. Wait for awhile before trying again."); @@ -107,7 +108,6 @@ function action(mode, type, selection) { } else { cm.getClient().getChannelServer().getMapFactory().getMap(925020010 + avDojo).resetMapObjects(); - cm.getClient().getChannelServer().resetDojo(925020010 + avDojo); cm.resetDojoEnergy(); cm.warp(925020010 + avDojo, 0); @@ -120,9 +120,11 @@ function action(mode, type, selection) { } else if (cm.getPlayer().getDojoStage() > 0) { dojoWarp = cm.getPlayer().getDojoStage(); cm.getPlayer().setDojoStage(0); - cm.sendYesNo("The last time you took the challenge by yourself, you went up to level #b" + dojoWarp + "#k. I can take you there right now. Do you want to go there? (Select #rNo#k to erase this record.)"); + + var stageWarp = ((dojoWarp / 6) | 0) * 5; + cm.sendYesNo("The last time you took the challenge by yourself, you went up to round #b" + stageWarp + "#k. I can take you there right now. Do you want to go there? (Select #rNo#k to erase this record.)"); } else { - var avDojo = cm.getClient().getChannelServer().getAvailableDojo(false); + var avDojo = cm.getClient().getChannelServer().ingressDojo(false, dojoWarp); if(avDojo < 0) { if(avDojo == -1) cm.sendOk("All Dojo's are being used already. Wait for awhile before trying again."); @@ -132,7 +134,6 @@ function action(mode, type, selection) { } else { var warpDojoMap = 925020000 + (dojoWarp + 1) * 100 + avDojo; cm.getClient().getChannelServer().resetDojoMap(warpDojoMap); - cm.getClient().getChannelServer().resetDojo(warpDojoMap); cm.resetDojoEnergy(); cm.warp(warpDojoMap, 0); @@ -164,14 +165,13 @@ function action(mode, type, selection) { cm.dispose(); return; } else { - var avDojo = cm.getClient().getChannelServer().getAvailableDojo(true, cm.getParty()); + var avDojo = cm.getClient().getChannelServer().ingressDojo(true, cm.getParty(), 0); if(avDojo < 0) { if(avDojo == -1) cm.sendOk("All Dojo's are being used already. Wait for awhile before trying again."); else cm.sendOk("Either your party is already using the Dojo or your party's allotted time on the Dojo has not expired yet. Wait for them to finish to enter."); } else { cm.getClient().getChannelServer().resetDojoMap(925030100 + avDojo); - cm.getClient().getChannelServer().resetDojo(925030100 + avDojo); cm.resetPartyDojoEnergy(); cm.warpParty(925030100 + avDojo); @@ -299,7 +299,7 @@ function action(mode, type, selection) { } } - avDojo = cm.getClient().getChannelServer().getAvailableDojo(hasParty, cm.getParty()); + avDojo = cm.getClient().getChannelServer().ingressDojo(hasParty, cm.getParty(), Math.floor((cm.getPlayer().getMap().getId()) / 100) % 100); firstEnter = true; } @@ -313,7 +313,6 @@ function action(mode, type, selection) { var dojoWarpMap = baseStg + (nextStg * 100) + avDojo; if(firstEnter) { cm.getClient().getChannelServer().resetDojoMap(dojoWarpMap); - cm.getClient().getChannelServer().resetDojo(dojoWarpMap, nextStg - 1); } //non-leader party members can progress whilst having the record saved if they don't command to enter the next stage diff --git a/scripts/npc/2091005_old.js b/scripts/npc/2091005_old.js index 028204db39..d5b543ee88 100644 --- a/scripts/npc/2091005_old.js +++ b/scripts/npc/2091005_old.js @@ -26,6 +26,8 @@ * @Map(s): Dojo Hall */ +importPackage(Packages.constants.game); + var disabled = false; var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004); var belt_level = Array(25, 35, 45, 60, 75); @@ -45,7 +47,7 @@ function start() { if (isRestingSpot(cm.getPlayer().getMap().getId())) { var text = "I'm surprised you made it this far! But it won't be easy from here on out. You still want the challenge?\r\n\r\n#b#L0#I want to continue#l\r\n#L1#I want to leave#l\r\n"; - if (!cm.getPlayer().getDojoParty()) { + if (!GameConstants.isDojoPartyArea(cm.getPlayer().getMapId())) { text += "#L2#I want to record my score up to this point#l"; } cm.sendSimple(text); diff --git a/scripts/npc/9000040.js b/scripts/npc/9000040.js index ecb1b32681..fe4770623f 100644 --- a/scripts/npc/9000040.js +++ b/scripts/npc/9000040.js @@ -24,7 +24,7 @@ * @author Ronan Lana */ -importPackage(Packages.client.processor); +importPackage(Packages.client.processor.action); importPackage(Packages.config); var status; diff --git a/scripts/npc/9209000.js b/scripts/npc/9209000.js index 2ae7c3c082..3feffe777f 100644 --- a/scripts/npc/9209000.js +++ b/scripts/npc/9209000.js @@ -87,30 +87,50 @@ function action(mode, type, selection) { } else if(status == 1) { var sendStr = "The following books are currently available:\r\n\r\n"; if(selected == 0) selected = selection; - - table = (selected == 1) ? skillbook : masterybook; - for(var i = 0; i < table.length; i++) { - sendStr += " #L" + i + "# #i" + table[i] + "# #t" + table[i] + "##l\r\n"; + + if (selected == 1) { + table = skillbook; + for(var i = 0; i < table.length; i++) { + if (table[i] > 0) { + var itemid = table[i]; + sendStr += " #L" + i + "# #i" + itemid + "# #t" + itemid + "##l\r\n"; + } else { + var skillid = -table[i]; + sendStr += " #L" + i + "# #s" + skillid + "# #q" + skillid + "##l\r\n"; + } + } + } else { + table = masterybook; + for(var i = 0; i < table.length; i++) { + var itemid = table[i]; + sendStr += " #L" + i + "# #i" + itemid + "# #t" + itemid + "##l\r\n"; + } } - + cm.sendSimple(sendStr); } else if(status == 2) { selected = selection; - var mobList = cm.getNamesWhoDropsItem(table[selected]); - - var sendStr; - if(mobList.length == 0) { - sendStr = "No mobs drop '#b#t" + table[selected] + "##k'.\r\n\r\n"; - } else { - sendStr = "The following mobs drop '#b#t" + table[selected] + "##k':\r\n\r\n"; - for(var i = 0; i < mobList.length; i++) { - sendStr += " #L" + i + "# " + mobList[i] + "#l\r\n"; - } + var sendStr; + if (table[selected] > 0) { + var mobList = cm.getNamesWhoDropsItem(table[selected]); - sendStr += "\r\n"; + if(mobList.length == 0) { + sendStr = "No mobs drop '#b#t" + table[selected] + "##k'.\r\n\r\n"; + } else { + sendStr = "The following mobs drop '#b#t" + table[selected] + "##k':\r\n\r\n"; + + for(var i = 0; i < mobList.length; i++) { + sendStr += " #L" + i + "# " + mobList[i] + "#l\r\n"; + } + + sendStr += "\r\n"; + } + } else { + sendStr = "\r\n\r\n"; } + sendStr += cm.getSkillBookInfo(table[selected]); cm.sendNext(sendStr); diff --git a/scripts/npc/credits.js b/scripts/npc/credits.js index 29db73a8fc..6bd080459f 100644 --- a/scripts/npc/credits.js +++ b/scripts/npc/credits.js @@ -13,7 +13,7 @@ var name_cursor, role_cursor; // new server names are to be appended at the start of the name stack, building up the chronology. // make sure the server names are lexicograffically equivalent to their correspondent function. -var servers = ["HeavenMS", "MapleSolaxia", "MoopleDEV", "MetroMS", "BubblesDEV", "OdinMS", "Contributors"]; +var servers = ["HeavenMS", "MapleSolaxia", "MoopleDEV", "BubblesDEV", "MetroMS", "OdinMS", "Contributors"]; var servers_history = []; function addPerson(name, role) { @@ -65,20 +65,20 @@ function writeServerStaff_MoopleDEV() { setHistory(2010, 2012); } -function writeServerStaff_MetroMS() { - addPerson("David!", "Developer"); - addPerson("XxOsirisxX", "Contributor"); - addPerson("Generic", "Contributor"); - - setHistory(2009, 2010); -} - function writeServerStaff_BubblesDEV() { addPerson("David!", "Developer"); addPerson("Moogra", "Developer"); addPerson("XxOsirisxX", "Contributor"); addPerson("MrMysterious", "Contributor"); + setHistory(2009, 2010); +} + +function writeServerStaff_MetroMS() { + addPerson("David!", "Developer"); + addPerson("XxOsirisxX", "Contributor"); + addPerson("Generic", "Contributor"); + setHistory(2009, 2009); } diff --git a/scripts/portal/dojang_tuto.js b/scripts/portal/dojang_tuto.js index d90a473d51..31378e49b5 100644 --- a/scripts/portal/dojang_tuto.js +++ b/scripts/portal/dojang_tuto.js @@ -29,6 +29,8 @@ function enter(pi) { if (pi.getPlayer().getMap().getMonsterById(9300216) != null) { pi.getPlayer().enteredScript("dojang_Msg", pi.getPlayer().getMap().getId()); pi.getPlayer().setFinishedDojoTutorial(); + pi.getClient().getChannelServer().resetDojo(pi.getPlayer().getMap().getId()); + pi.getClient().getChannelServer().dismissDojoSchedule(pi.getPlayer().getMap().getId(), pi.getParty()); pi.playPortalSound(); pi.warp(925020001, 0); return true; } else { diff --git a/scripts/portal/dojang_up.js b/scripts/portal/dojang_up.js index 776510f547..e3f60ef36f 100644 --- a/scripts/portal/dojang_up.js +++ b/scripts/portal/dojang_up.js @@ -25,6 +25,7 @@ * @maps: All Dojo fighting maps */ +importPackage(Packages.constants.game); function enter(pi) { try { @@ -32,7 +33,7 @@ function enter(pi) { pi.goDojoUp(); pi.getPlayer().getMap().setReactorState(); var stage = Math.floor(pi.getPlayer().getMapId() / 100) % 100; - if ((stage - (stage / 6) | 0) == pi.getPlayer().getVanquisherStage() && !pi.getPlayer().getDojoParty()) // we can also try 5 * stage / 6 | 0 + 1 + if ((stage - (stage / 6) | 0) == pi.getPlayer().getVanquisherStage() && !GameConstants.isDojoPartyArea(pi.getPlayer().getMapId())) // we can also try 5 * stage / 6 | 0 + 1 pi.getPlayer().setVanquisherKills(pi.getPlayer().getVanquisherKills() + 1); } else { pi.getPlayer().message("There are still some monsters remaining."); diff --git a/scripts/portal/s4rush.js b/scripts/portal/s4rush.js index 023009a303..d79ce0fcfd 100644 --- a/scripts/portal/s4rush.js +++ b/scripts/portal/s4rush.js @@ -25,15 +25,8 @@ */ function enter(pi) { if(pi.isQuestStarted(6110)) { - if(pi.getWarpMap(910500100).countPlayers() == 0) { - pi.resetMapObjects(910500100); - pi.playPortalSound(); pi.warp(910500100, 0); - - return true; - } else { - pi.getPlayer().message("Some other player is currently inside."); - return false; - } + pi.playPortalSound(); pi.warp(910500100, 0); + return true; } else { pi.getPlayer().message("A mysterious force won't let you in."); return false; diff --git a/sql/db_database.sql b/sql/db_database.sql index 393df40625..85f55c30f2 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -6013,7 +6013,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (7130000, 1072210, 1, 1, 0, 800), (7130000, 1002275, 1, 1, 0, 1500), (7130000, 1072177, 1, 1, 0, 800), -(7130000, 4161021, 1, 1, 0, 6000), +(7130000, 4161021, 1, 1, 0, 7000), (7130000, 1072312, 1, 1, 0, 800), (7130000, 2044901, 1, 1, 0, 300), (7130000, 2040419, 1, 1, 0, 300), @@ -7120,7 +7120,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (8140102, 1452019, 1, 1, 0, 500), (8140102, 1382035, 1, 1, 0, 700), (8140102, 1432004, 1, 1, 0, 500), -(8140102, 4161015, 1, 1, 0, 6000), +(8140102, 4161015, 1, 1, 0, 7000), (8140102, 1002643, 1, 1, 0, 1500), (8140102, 2331000, 1, 1, 0, 500), (8140102, 2040321, 1, 1, 0, 300), @@ -7152,7 +7152,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (8140103, 1322045, 1, 1, 0, 700), (8140103, 1412021, 1, 1, 0, 700), (8140103, 1432011, 1, 1, 0, 500), -(8140103, 4161016, 1, 1, 0, 6000), +(8140103, 4161016, 1, 1, 0, 7000), (8140103, 1492010, 1, 1, 0, 500), (8140103, 2332000, 1, 1, 0, 500), (8140103, 2044314, 1, 1, 0, 300), @@ -7713,7 +7713,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (8150100, 2041013, 1, 1, 0, 300), (8150100, 1002366, 1, 1, 0, 1500), (8150100, 1072214, 1, 1, 0, 800), -(8150100, 4161018, 1, 1, 0, 6000), +(8150100, 4161018, 1, 1, 0, 7000), (8150100, 1072315, 1, 1, 0, 800), (8150100, 1052131, 1, 1, 0, 700), (8150100, 2044902, 1, 1, 0, 300), @@ -7745,7 +7745,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (8150101, 1072223, 1, 1, 0, 800), (8150101, 2290042, 1, 1, 0, 500), (8150101, 2290052, 1, 1, 0, 500), -(8150101, 4161018, 1, 1, 0, 6000), +(8150101, 4161018, 1, 1, 0, 7000), (8150101, 1072318, 1, 1, 0, 800), (8150101, 2290102, 1, 1, 0, 500), (8150101, 2040420, 1, 1, 0, 300), @@ -10287,10 +10287,6 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (8190000, 0, 800, 1200, 0, 400000), (8190002, 0, 900, 1300, 0, 400000), (9400545, 0, 600, 900, 0, 400000), -(9001000, 0, 200, 400, 0, 400000), -(9001001, 0, 200, 400, 0, 400000), -(9001002, 0, 200, 400, 0, 400000), -(9001003, 0, 200, 400, 0, 400000), (9420500, 0, 36, 54, 0, 400000), (9420502, 0, 30, 42, 0, 400000), (9420506, 0, 56, 69, 0, 400000), @@ -10735,14 +10731,14 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi (8500002, 4031869, 1, 1, 6360, 999999), (8141000, 4031873, 1, 1, 6380, 60000), (8141100, 4031874, 1, 1, 6390, 60000), -(7130101, 4001112, 1, 1, 0, 1000), -(8170000, 4001112, 1, 1, 0, 1500), -(5130107, 4001107, 1, 1, 0, 1000), -(8143000, 4001107, 1, 1, 0, 1500), -(7160000, 4001110, 1, 1, 0, 1000), -(8150100, 4161018, 1, 1, 0, 1000), -(7130000, 4161021, 1, 1, 0, 1000), -(8150000, 4001111, 1, 1, 0, 999999), +(7130101, 4001112, 1, 1, 0, 7000), +(8170000, 4001112, 1, 1, 0, 7500), +(5130107, 4001107, 1, 1, 0, 7000), +(8143000, 4001107, 1, 1, 0, 7500), +(7160000, 4001110, 1, 1, 0, 7000), +(8150100, 4161018, 1, 1, 0, 7000), +(7130000, 4161021, 1, 1, 0, 7000), +(8150000, 4001111, 1, 1, 0, 700000), (8140000, 4031477, 1, 1, 0, 10000), (8170000, 4031453, 1, 1, 6291, 50000), (8160000, 4031474, 1, 1, 6295, 50000), @@ -20207,7 +20203,7 @@ INSERT INTO `shopitems` (`shopitemid`, `shopid`, `itemid`, `price`, `pitch`, `po (2755, 9201059, 1312005, 42500, 0, 156), (2756, 9201059, 1302068, 352500, 0, 160), (2757, 9201059, 1302008, 42500, 0, 164), -(2758, 9201060, 2010003, 100, 0, 104), +(2758, 9201060, 2010003, 100, 0, 178), (2759, 9201060, 2061000, 1, 0, 108), (2760, 9201060, 2060000, 1, 0, 112), (2761, 9201060, 2030000, 400, 0, 116), diff --git a/sql/db_drops.sql b/sql/db_drops.sql index d17bc0adde..a9eb80b99a 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -20278,8 +20278,8 @@ USE `heavenms`; (4300008, 4032521, 1, 1, 2291, 120000), (4300009, 4032521, 1, 1, 2291, 120000), (4300010, 4032521, 1, 1, 2291, 120000), -(5130107, 4001207, 1, 1, 0, 2285), -(5130108, 4001207, 1, 1, 0, 2285), +(5130107, 4001207, 1, 1, 20402, 100000), +(5130108, 4001207, 1, 1, 20402, 100000), (9400506, 2020020, 1, 1, 0, 100000), (9400506, 4031217, 1, 1, 0, 40000), (9400507, 2020020, 1, 1, 0, 100000), @@ -22844,8 +22844,6 @@ DELETE FROM temp_data WHERE dropperid >= 9300315 AND dropperid <= 9300324; (8220009, 0, 1479, 7280, 0, 400000), (8830000, 0, 2400, 11620, 0, 400000), (8830007, 0, 2400, 11620, 0, 400000), -(9001009, 0, 1254, 6170, 0, 400000), -(9001011, 0, 95, 140, 0, 400000), (9200016, 0, 81, 119, 0, 400000), (9200019, 0, 203, 299, 0, 400000), (9300011, 0, 109, 160, 0, 400000), @@ -22959,9 +22957,7 @@ DELETE FROM temp_data WHERE dropperid >= 9300315 AND dropperid <= 9300324; (9300269, 0, 174, 850, 0, 400000), (9300270, 0, 418, 617, 0, 400000), (9300274, 0, 39, 57, 0, 400000), -(9300289, 0, 1704, 8530, 0, 400000), -(9300294, 0, 2142, 10490, 0, 400000), -(9300315, 0, 483, 2370, 0, 400000), +(9300315, 0, 483, 2370, 0, 400000), # thanks Vcoc for noticing some Cygnus questline bosses dropping mesos (9300316, 0, 516, 2540, 0, 400000), (9300317, 0, 552, 2710, 0, 400000), (9300318, 0, 588, 2890, 0, 400000), diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index f8f09d86ab..f23d08e6c0 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -144,6 +144,7 @@ import client.processor.action.PetAutopotProcessor; import constants.game.ExpTable; import constants.game.GameConstants; import constants.inventory.ItemConstants; +import constants.net.ServerConstants; import constants.skills.Aran; import constants.skills.Beginner; import constants.skills.Bishop; @@ -279,8 +280,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private Map summons = new LinkedHashMap<>(); private Map coolDowns = new LinkedHashMap<>(); private EnumMap> diseases = new EnumMap<>(MapleDisease.class); - public byte[] m_aQuickslotLoaded; - public MapleQuickslotBinding m_pQuickslotKeyMapped; + private byte[] m_aQuickslotLoaded; + private MapleQuickslotBinding m_pQuickslotKeyMapped; private MapleDoor pdoor = null; private Map questExpirations = new LinkedHashMap<>(); private ScheduledFuture dragonBloodSchedule; @@ -678,7 +679,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { int pts = 0; if (dojoPoints < 17000) { pts = 1 + ((mapid - 1) / 100 % 100) / 6; - if (!getDojoParty()) { + if (!GameConstants.isDojoPartyArea(this.getMapId())) { pts++; } this.dojoPoints += pts; @@ -1322,6 +1323,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } + public void changeQuickslotKeybinding(byte[] aQuickslotKeyMapped) { + this.m_pQuickslotKeyMapped = new MapleQuickslotBinding(aQuickslotKeyMapped); + } + public void broadcastStance(int newStance) { setStance(newStance); broadcastStance(); @@ -1820,6 +1825,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } else { FilePrinter.printError(FilePrinter.MAPLE_MAP, "Character " + this.getName() + " got stuck when moving to map " + map.getId() + "."); + client.disconnect(true, false); // thanks BHB for noticing a player storage stuck case here + return; } notifyMapTransferToPartner(map.getId()); @@ -2804,9 +2811,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { dispelDebuff(MapleDisease.POISON); dispelDebuff(MapleDisease.SEAL); dispelDebuff(MapleDisease.WEAKEN); - dispelDebuff(MapleDisease.SLOW); - dispelDebuff(MapleDisease.ZOMBIFY); - dispelDebuff(MapleDisease.CONFUSE); + dispelDebuff(MapleDisease.SLOW); // thanks Conrad for noticing ZOMBIFY isn't dispellable } public void cancelAllDebuffs() { @@ -4772,10 +4777,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject { return dojoEnergy; } - public boolean getDojoParty() { - return mapid >= 925030100 && mapid < 925040000; - } - public int getDojoPoints() { return dojoPoints; } @@ -5322,6 +5323,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public MapleRing getMarriageRing() { return partnerId > 0 ? marriageRing : null; } + + public int getMasterLevel(int skill) { + SkillEntry ret = skills.get(SkillFactory.getSkill(skill)); + if (ret == null) { + return 0; + } + return ret.masterlevel; + } public int getMasterLevel(Skill skill) { if (skills.get(skill) == null) { @@ -8351,9 +8360,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ps.close(); // No quickslots, or no change. - boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped, this.m_aQuickslotLoaded)); + boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.GetKeybindings(), this.m_aQuickslotLoaded)); if (!bQuickslotEquals) { - long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped); + long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.GetKeybindings()); try (final PreparedStatement pInsertStatement = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) { pInsertStatement.setInt(1, this.getAccountID()); @@ -8616,9 +8625,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject { ps.close(); // No quickslots, or no change. - boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped, this.m_aQuickslotLoaded)); + boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.GetKeybindings(), this.m_aQuickslotLoaded)); if (!bQuickslotEquals) { - long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped); + long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.GetKeybindings()); try (final PreparedStatement pInsertStatement = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) { pInsertStatement.setInt(1, this.getAccountID()); @@ -9214,9 +9223,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { MapleKeyBinding autohpPot = this.getKeymap().get(91); if (autohpPot != null) { int autohpItemid = autohpPot.getAction(); - if (((float) this.getHp()) / this.getCurrentMaxHp() <= this.getAutopotHpAlert()) { // try within user settings... thanks Lame, Optimist, Stealth2800 + float autohpAlert = this.getAutopotHpAlert(); + if (((float) this.getHp()) / this.getCurrentMaxHp() <= autohpAlert) { // try within user settings... thanks Lame, Optimist, Stealth2800 Item autohpItem = this.getInventory(MapleInventoryType.USE).findById(autohpItemid); if (autohpItem != null) { + this.setAutopotHpAlert(0.9f * autohpAlert); PetAutopotProcessor.runAutopotAction(client, autohpItem.getPosition(), autohpItemid); } } @@ -9227,9 +9238,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { MapleKeyBinding autompPot = this.getKeymap().get(92); if (autompPot != null) { int autompItemid = autompPot.getAction(); - if (((float) this.getMp()) / this.getCurrentMaxMp() <= this.getAutopotMpAlert()) { + float autompAlert = this.getAutopotMpAlert(); + if (((float) this.getMp()) / this.getCurrentMaxMp() <= autompAlert) { Item autompItem = this.getInventory(MapleInventoryType.USE).findById(autompItemid); if (autompItem != null) { + this.setAutopotMpAlert(0.9f * autompAlert); // autoMP would stick to using pots at every depletion in some cases... thanks Rohenn PetAutopotProcessor.runAutopotAction(client, autompItem.getPosition(), autompItemid); } } @@ -9689,17 +9702,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void showDojoClock() { - if (map.isDojoFightMap()) { + if (GameConstants.isDojoBossArea(map.getId())) { client.announce(MaplePacketCreator.getClock((int) (getDojoTimeLeft() / 1000))); } } - public void timeoutFromDojo() { - if(map.isDojoMap()) { - client.getPlayer().changeMap(client.getChannelServer().getMapFactory().getMap(925020002)); - } - } - public void showUnderleveledInfo(MapleMonster mob) { long curTime = Server.getInstance().getCurrentTime(); if(nextWarningTime < curTime) { diff --git a/src/client/inventory/MapleInventory.java b/src/client/inventory/MapleInventory.java index a5d6529aed..f910a4d903 100644 --- a/src/client/inventory/MapleInventory.java +++ b/src/client/inventory/MapleInventory.java @@ -25,12 +25,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Map; +import java.util.Set; import java.util.concurrent.locks.Lock; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; @@ -448,6 +450,20 @@ public class MapleInventory implements Iterable { } } + private static boolean checkItemRestricted(List> items) { + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); + + Set itemids = new HashSet<>(); + for (Pair p : items) { + int itemid = p.getLeft().getItemId(); + if (ii.isPickupRestricted(itemid) && (p.getLeft().getQuantity() > 1 || !itemids.add(itemid))) { + return false; + } + } + + return true; + } + public static boolean checkSpot(MapleCharacter chr, Item item) { // thanks Vcoc for noticing pshops not checking item stacks when taking item back return checkSpot(chr, Collections.singletonList(item)); } @@ -476,6 +492,10 @@ public class MapleInventory implements Iterable { public static boolean checkSpots(MapleCharacter chr, List> items, List typesSlotsUsed, boolean useProofInv) { // assumption: no "UNDEFINED" or "EQUIPPED" items shall be tested here, all counts are >= 0. + if (!checkItemRestricted(items)) { + return false; + } + Map> rcvItems = new LinkedHashMap<>(); Map rcvTypes = new LinkedHashMap<>(); @@ -490,7 +510,7 @@ public class MapleInventory implements Iterable { rcvItems.put(itemId, itemQtyList); rcvTypes.put(itemId, item.right.getType()); } else { - if (!ItemConstants.isRechargeable(itemId)) { + if (!ItemConstants.isEquipment(itemId) && !ItemConstants.isRechargeable(itemId)) { qty.set(0, qty.get(0) + item.left.getQuantity()); } else { qty.add((int) item.left.getQuantity()); @@ -548,6 +568,10 @@ public class MapleInventory implements Iterable { public static boolean checkSpotsAndOwnership(MapleCharacter chr, List> items, List typesSlotsUsed, boolean useProofInv) { //assumption: no "UNDEFINED" or "EQUIPPED" items shall be tested here, all counts are >= 0 and item list to be checked is a legal one. + if (!checkItemRestricted(items)) { + return false; + } + Map> rcvItems = new LinkedHashMap<>(); Map rcvTypes = new LinkedHashMap<>(); Map rcvOwners = new LinkedHashMap<>(); @@ -565,7 +589,7 @@ public class MapleInventory implements Iterable { rcvOwners.put(itemHash, item.left.getOwner()); } else { // thanks BHB88 for pointing out an issue with rechargeable items being stacked on inventory check - if (!ItemConstants.isRechargeable(item.left.getItemId())) { + if (!ItemConstants.isEquipment(item.left.getItemId()) && !ItemConstants.isRechargeable(item.left.getItemId())) { qty.set(0, qty.get(0) + item.left.getQuantity()); } else { qty.add((int) item.left.getQuantity()); diff --git a/src/client/inventory/manipulator/MapleInventoryManipulator.java b/src/client/inventory/manipulator/MapleInventoryManipulator.java index 68ce8d698b..14428fd4b0 100644 --- a/src/client/inventory/manipulator/MapleInventoryManipulator.java +++ b/src/client/inventory/manipulator/MapleInventoryManipulator.java @@ -106,7 +106,7 @@ public class MapleInventoryManipulator { } } boolean sandboxItem = (flag & ItemConstants.SANDBOX) == ItemConstants.SANDBOX; - while (quantity > 0 || ItemConstants.isRechargeable(itemId)) { + while (quantity > 0) { short newQ = (short) Math.min(quantity, slotMax); if (newQ != 0) { quantity -= newQ; @@ -124,9 +124,6 @@ public class MapleInventoryManipulator { } c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem)))); if(sandboxItem) chr.setHasSandboxItem(); - if ((ItemConstants.isRechargeable(itemId)) && quantity == 0) { - break; - } } else { c.announce(MaplePacketCreator.enableActions()); return false; @@ -189,8 +186,8 @@ public class MapleInventoryManipulator { private static boolean addFromDropInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, Item item, boolean show, int petId) { MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); - - if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) { + int itemid = item.getItemId(); + if (ii.isPickupRestricted(itemid) && chr.haveItemWithId(itemid, true)) { c.announce(MaplePacketCreator.getInventoryFull()); c.announce(MaplePacketCreator.showItemUnavailable()); return false; @@ -198,9 +195,9 @@ public class MapleInventoryManipulator { short quantity = item.getQuantity(); if (!type.equals(MapleInventoryType.EQUIP)) { - short slotMax = ii.getSlotMax(c, item.getItemId()); - List existing = inv.listById(item.getItemId()); - if (!ItemConstants.isRechargeable(item.getItemId()) && petId == -1) { + short slotMax = ii.getSlotMax(c, itemid); + List existing = inv.listById(itemid); + if (!ItemConstants.isRechargeable(itemid) && petId == -1) { if (existing.size() > 0) { // first update all existing slots to slotMax Iterator i = existing.iterator(); while (quantity > 0) { @@ -222,7 +219,7 @@ public class MapleInventoryManipulator { while (quantity > 0) { short newQ = (short) Math.min(quantity, slotMax); quantity -= newQ; - Item nItem = new Item(item.getItemId(), (short) 0, newQ, petId); + Item nItem = new Item(itemid, (short) 0, newQ, petId); nItem.setExpiration(item.getExpiration()); nItem.setOwner(item.getOwner()); nItem.setFlag(item.getFlag()); @@ -239,7 +236,7 @@ public class MapleInventoryManipulator { if (MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem(); } } else { - Item nItem = new Item(item.getItemId(), (short) 0, quantity, petId); + Item nItem = new Item(itemid, (short) 0, quantity, petId); nItem.setExpiration(item.getExpiration()); nItem.setFlag(item.getFlag()); @@ -266,13 +263,13 @@ public class MapleInventoryManipulator { c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item)))); if (MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem(); } else { - FilePrinter.printError(FilePrinter.ITEM, "Tried to pickup Equip id " + item.getItemId() + " containing more than 1 quantity --> " + quantity); + FilePrinter.printError(FilePrinter.ITEM, "Tried to pickup Equip id " + itemid + " containing more than 1 quantity --> " + quantity); c.announce(MaplePacketCreator.getInventoryFull()); c.announce(MaplePacketCreator.showItemUnavailable()); return false; } if (show) { - c.announce(MaplePacketCreator.getShowItemGain(item.getItemId(), item.getQuantity())); + c.announce(MaplePacketCreator.getShowItemGain(itemid, item.getQuantity())); } return true; } diff --git a/src/client/keybind/MapleQuickslotBinding.java b/src/client/keybind/MapleQuickslotBinding.java index 5dc6aad345..f592774abb 100644 --- a/src/client/keybind/MapleQuickslotBinding.java +++ b/src/client/keybind/MapleQuickslotBinding.java @@ -17,7 +17,7 @@ public class MapleQuickslotBinding 0x2A, 0x52, 0x47, 0x49, 0x1D, 0x53, 0x4F, 0x51 }; - public byte[] m_aQuickslotKeyMapped; + private byte[] m_aQuickslotKeyMapped; // Initializes quickslot object for the user. // aKeys' length has to be 8. @@ -52,4 +52,9 @@ public class MapleQuickslotBinding oPacket.writeInt(nKey); } } + + public byte[] GetKeybindings() { + return m_aQuickslotKeyMapped; + } + } \ No newline at end of file diff --git a/src/client/processor/action/MakerProcessor.java b/src/client/processor/action/MakerProcessor.java index 16f87cdfde..4ce7f25b1b 100644 --- a/src/client/processor/action/MakerProcessor.java +++ b/src/client/processor/action/MakerProcessor.java @@ -308,7 +308,7 @@ public class MakerProcessor { } private static short getCreateStatus(MapleClient c, MakerItemCreateEntry recipe) { - if(recipe == null) { + if(recipe.isInvalid()) { return -1; } diff --git a/src/constants/game/GameConstants.java b/src/constants/game/GameConstants.java index ea8174cfe4..e916af64bf 100644 --- a/src/constants/game/GameConstants.java +++ b/src/constants/game/GameConstants.java @@ -585,6 +585,15 @@ public class GameConstants { return mapid >= 925020000 && mapid < 925040000; } + public static boolean isDojoPartyArea(int mapid) { + return mapid >= 925030100 && mapid < 925040000; + } + + public static boolean isDojoBossArea(int mapid) { + return isDojo(mapid) && (((mapid / 100) % 100) % 6) > 0; + } + + public static boolean isPyramid(int mapid) { return mapid >= 926010010 & mapid <= 930010000; } diff --git a/src/net/server/Server.java b/src/net/server/Server.java index b8f0e62a50..108ad0491c 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -320,7 +320,7 @@ public class Server { private void dumpData() { - wldWLock.lock(); + wldRLock.lock(); try { System.out.println(worlds); System.out.println(channels); @@ -328,34 +328,44 @@ public class Server { System.out.println(); System.out.println("---------------------"); } finally { - wldWLock.unlock(); + wldRLock.unlock(); } } public int addChannel(int worldid) { - wldWLock.lock(); + World world; + Map channelInfo; + int channelid; + + wldRLock.lock(); try { if(worldid >= worlds.size()) return -3; - Map worldChannels = channels.get(worldid); - if(worldChannels == null) return -3; + channelInfo = channels.get(worldid); + if(channelInfo == null) return -3; - int channelid = worldChannels.size(); + channelid = channelInfo.size(); if(channelid >= YamlConfig.config.server.CHANNEL_SIZE) return -2; channelid++; - World world = this.getWorld(worldid); - Channel channel = new Channel(worldid, channelid, getCurrentTime()); - - channel.setServerMessage(YamlConfig.config.worlds.get(worldid).why_am_i_recommended); - - world.addChannel(channel); - worldChannels.put(channelid, channel.getIP()); - - return channelid; + world = this.getWorld(worldid); } finally { - wldWLock.unlock(); + wldRLock.unlock(); } + + Channel channel = new Channel(worldid, channelid, getCurrentTime()); + channel.setServerMessage(YamlConfig.config.worlds.get(worldid).why_am_i_recommended); + + if (world.addChannel(channel)) { + wldWLock.lock(); + try { + channelInfo.put(channelid, channel.getIP()); + } finally { + wldWLock.unlock(); + } + } + + return channelid; } public int addWorld() { @@ -380,73 +390,96 @@ public class Server { } private int initWorld() { - wldWLock.lock(); + int i; + + wldRLock.lock(); try { - int i = worlds.size(); + i = worlds.size(); if(i >= YamlConfig.config.server.WLDLIST_SIZE) { return -1; } - - System.out.println("Starting world " + i); + } finally { + wldRLock.unlock(); + } + + System.out.println("Starting world " + i); - int exprate = YamlConfig.config.worlds.get(i).exp_rate; - int mesorate = YamlConfig.config.worlds.get(i).meso_rate; - int droprate = YamlConfig.config.worlds.get(i).drop_rate; - int bossdroprate = YamlConfig.config.worlds.get(i).boss_drop_rate; - int questrate = YamlConfig.config.worlds.get(i).quest_rate; - int travelrate = YamlConfig.config.worlds.get(i).travel_rate; - int fishingrate = YamlConfig.config.worlds.get(i).fishing_rate; + int exprate = YamlConfig.config.worlds.get(i).exp_rate; + int mesorate = YamlConfig.config.worlds.get(i).meso_rate; + int droprate = YamlConfig.config.worlds.get(i).drop_rate; + int bossdroprate = YamlConfig.config.worlds.get(i).boss_drop_rate; + int questrate = YamlConfig.config.worlds.get(i).quest_rate; + int travelrate = YamlConfig.config.worlds.get(i).travel_rate; + int fishingrate = YamlConfig.config.worlds.get(i).fishing_rate; - int flag = YamlConfig.config.worlds.get(i).flag; - String event_message = YamlConfig.config.worlds.get(i).event_message; - String why_am_i_recommended = YamlConfig.config.worlds.get(i).why_am_i_recommended; - - World world = new World(i, - flag, - event_message, - exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate); + int flag = YamlConfig.config.worlds.get(i).flag; + String event_message = YamlConfig.config.worlds.get(i).event_message; + String why_am_i_recommended = YamlConfig.config.worlds.get(i).why_am_i_recommended; - worldRecommendedList.add(new Pair<>(i, why_am_i_recommended)); - worlds.add(world); + World world = new World(i, + flag, + event_message, + exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate); + + Map channelInfo = new HashMap<>(); + long bootTime = getCurrentTime(); + for (int j = 1; j <= YamlConfig.config.worlds.get(i).channels; j++) { + int channelid = j; + Channel channel = new Channel(i, channelid, bootTime); - Map channelInfo = new HashMap<>(); - long bootTime = getCurrentTime(); - for (int j = 1; j <= YamlConfig.config.worlds.get(i).channels; j++) { - int channelid = j; - Channel channel = new Channel(i, channelid, bootTime); - - world.addChannel(channel); - channelInfo.put(channelid, channel.getIP()); + world.addChannel(channel); + channelInfo.put(channelid, channel.getIP()); + } + + boolean canDeploy; + + wldWLock.lock(); // thanks Ashen for noticing a deadlock issue when trying to deploy a channel + try { + canDeploy = world.getId() == worlds.size(); + if (canDeploy) { + worldRecommendedList.add(new Pair<>(i, why_am_i_recommended)); + worlds.add(world); + channels.add(i, channelInfo); } - - channels.add(i, channelInfo); - - world.setServerMessage(YamlConfig.config.worlds.get(i).server_message); - System.out.println("Finished loading world " + i + "\r\n"); - - return i; } finally { wldWLock.unlock(); } + + if (canDeploy) { + world.setServerMessage(YamlConfig.config.worlds.get(i).server_message); + + System.out.println("Finished loading world " + i + "\r\n"); + return i; + } else { + System.out.println("Could not load world " + i + "...\r\n"); + world.shutdown(); + return -2; + } } public boolean removeChannel(int worldid) { //lol don't! - wldWLock.lock(); + World world; + + wldRLock.lock(); try { if(worldid >= worlds.size()) return false; - - World world = worlds.get(worldid); - if (world != null) { - int channel = world.removeChannel(); - + world = worlds.get(worldid); + } finally { + wldRLock.unlock(); + } + + if (world != null) { + int channel = world.removeChannel(); + wldWLock.lock(); + try { Map m = channels.get(worldid); if(m != null) m.remove(channel); - - return channel > -1; + } finally { + wldWLock.unlock(); } - } finally { - wldWLock.unlock(); + + return channel > -1; } return false; @@ -472,17 +505,15 @@ public class Server { return false; } + removeWorldPlayerRanking(); + w.shutdown(); + wldWLock.lock(); try { - if(worldid == worlds.size() - 1) { - removeWorldPlayerRanking(); - w.shutdown(); - + if (worldid == worlds.size() - 1) { worlds.remove(worldid); channels.remove(worldid); worldRecommendedList.remove(worldid); - } else { - return false; } } finally { wldWLock.unlock(); @@ -714,7 +745,7 @@ public class Server { if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) { wldWLock.lock(); try { - if(playerRanking.size() < this.getWorldsSize()) { + if(playerRanking.size() < worlds.size()) { return; } diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java index df6b7440f0..9d3ff78bd5 100644 --- a/src/net/server/channel/Channel.java +++ b/src/net/server/channel/Channel.java @@ -67,6 +67,7 @@ import org.apache.mina.transport.socket.SocketSessionConfig; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import client.MapleCharacter; +import constants.game.GameConstants; import net.server.services.ServicesManager; import net.server.services.BaseService; import net.server.services.type.ChannelServices; @@ -97,18 +98,18 @@ public final class Channel { private final Map storedVars = new HashMap<>(); private Set playersAway = new HashSet<>(); private Map expeditions = new HashMap<>(); + private Map dungeons = new HashMap<>(); private List expedType = new ArrayList<>(); private Set ownedMaps = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap())); private MapleEvent event; private boolean finishedShutdown = false; - private int usedDojo = 0; private Set usedMC = new HashSet<>(); + private int usedDojo = 0; private int[] dojoStage; private long[] dojoFinishTime; private ScheduledFuture[] dojoTask; private Map dojoParty = new HashMap<>(); - private Map dungeons = new HashMap<>(); private List chapelReservationQueue = new LinkedList<>(); private List cathedralReservationQueue = new LinkedList<>(); @@ -226,11 +227,16 @@ public final class Channel { } private void closeChannelSchedules() { - for(int i = 0; i < 20; i++) { - if(dojoTask[i] != null) { - dojoTask[i].cancel(false); - dojoTask[i] = null; + lock.lock(); + try { + for(int i = 0; i < dojoTask.length; i++) { + if(dojoTask[i] != null) { + dojoTask[i].cancel(false); + dojoTask[i] = null; + } } + } finally { + lock.unlock(); } closeChannelServices(); @@ -493,43 +499,54 @@ public final class Channel { this.storedVars.put(key, val); } - public synchronized int lookupPartyDojo(MapleParty party) { + public int lookupPartyDojo(MapleParty party) { if(party == null) return -1; Integer i = dojoParty.get(party.hashCode()); return (i != null) ? i : -1; } - public int getAvailableDojo(boolean isPartyDojo) { - return getAvailableDojo(isPartyDojo, null); + public int ingressDojo(boolean isPartyDojo, int fromStage) { + return ingressDojo(isPartyDojo, null, fromStage); } - public synchronized int getAvailableDojo(boolean isPartyDojo, MapleParty party) { - int dojoList = this.usedDojo; - int range, slot = 0; - - if(!isPartyDojo) { - dojoList = dojoList >> 5; - range = 15; - } else { - range = 5; - } - - while((dojoList & 1) != 0) { - dojoList = (dojoList >> 1); - slot++; - } - - if(slot < range) { - if(party != null) { - if(dojoParty.containsKey(party.hashCode())) return -2; - dojoParty.put(party.hashCode(), slot); + public int ingressDojo(boolean isPartyDojo, MapleParty party, int fromStage) { + lock.lock(); + try { + int dojoList = this.usedDojo; + int range, slot = 0; + + if(!isPartyDojo) { + dojoList = dojoList >> 5; + range = 15; + } else { + range = 5; } + + while((dojoList & 1) != 0) { + dojoList = (dojoList >> 1); + slot++; + } + + if(slot < range) { + int slotMapid = (isPartyDojo ? 925030000 : 925020000) + (100 * (fromStage + 1)) + slot; + int dojoSlot = getDojoSlot(slotMapid); - this.usedDojo |= (1 << ((!isPartyDojo ? 5 : 0) + slot)); - return slot; - } else { - return -1; + if(party != null) { + if(dojoParty.containsKey(party.hashCode())) return -2; + dojoParty.put(party.hashCode(), dojoSlot); + } + + this.usedDojo |= (1 << dojoSlot); + + this.resetDojo(slotMapid); + this.startDojoSchedule(slotMapid); + return slot; + } else { + return -1; + } + } finally { + lock.unlock(); } } @@ -537,13 +554,19 @@ public final class Channel { int mask = 0b11111111111111111111; mask ^= (1 << slot); - usedDojo &= mask; + lock.lock(); + try { + usedDojo &= mask; + } finally { + lock.unlock(); + } + if(party != null) { if(dojoParty.remove(party.hashCode()) != null) return; } if(dojoParty.containsValue(slot)) { // strange case, no party there! - Set> es = Collections.unmodifiableSet(dojoParty.entrySet()); + Set> es = new HashSet<>(dojoParty.entrySet()); for(Entry e: es) { if(e.getValue() == slot) { @@ -565,17 +588,12 @@ public final class Channel { } public void resetDojo(int dojoMapId) { - resetDojo(dojoMapId, 0); + resetDojo(dojoMapId, -1); } - public void resetDojo(int dojoMapId, int thisStg) { + private void resetDojo(int dojoMapId, int thisStg) { int slot = getDojoSlot(dojoMapId); this.dojoStage[slot] = thisStg; - - if(this.dojoTask[slot] != null) { - this.dojoTask[slot].cancel(false); - this.dojoTask[slot] = null; - } } public void freeDojoSectionIfEmpty(int dojoMapId) { @@ -595,34 +613,45 @@ public final class Channel { freeDojoSlot(slot, null); } - public void startDojoSchedule(final int dojoMapId) { + private void startDojoSchedule(final int dojoMapId) { final int slot = getDojoSlot(dojoMapId); final int stage = (dojoMapId / 100) % 100; if(stage <= dojoStage[slot]) return; long clockTime = (stage > 36 ? 15 : (stage / 6) + 5) * 60000; - this.dojoTask[slot] = TimerManager.getInstance().schedule(new Runnable() { - @Override - public void run() { - final int delta = (dojoMapId) % 100; - final int dojoBaseMap = (slot < 5) ? 925030000 : 925020000; - MapleParty party = null; - - for (int i = 0; i < 5; i++) { //only 32 stages, but 38 maps - if (stage + i > 38) { - break; - } - for(MapleCharacter chr: getMapFactory().getMap(dojoBaseMap + (100 * (stage + i)) + delta).getAllPlayers()) { - if(chr.getMap().isDojoMap()) { - chr.timeoutFromDojo(); - } - party = chr.getParty(); - } - } - - freeDojoSlot(slot, party); + + lock.lock(); + try { + if (this.dojoTask[slot] != null) { + this.dojoTask[slot].cancel(false); } - }, clockTime + 3000); // let the TIMES UP display for 3 seconds, then warp + this.dojoTask[slot] = TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + final int delta = (dojoMapId) % 100; + final int dojoBaseMap = (slot < 5) ? 925030000 : 925020000; + MapleParty party = null; + + for (int i = 0; i < 5; i++) { //only 32 stages, but 38 maps + if (stage + i > 38) { + break; + } + + MapleMap dojoExit = getMapFactory().getMap(925020002); + for(MapleCharacter chr: getMapFactory().getMap(dojoBaseMap + (100 * (stage + i)) + delta).getAllPlayers()) { + if(GameConstants.isDojo(chr.getMap().getId())) { + chr.changeMap(dojoExit); + } + party = chr.getParty(); + } + } + + freeDojoSlot(slot, party); + } + }, clockTime + 3000); // let the TIMES UP display for 3 seconds, then warp + } finally { + lock.unlock(); + } dojoFinishTime[slot] = Server.getInstance().getCurrentTime() + clockTime; } @@ -632,9 +661,14 @@ public final class Channel { int stage = (dojoMapId / 100) % 100; if(stage <= dojoStage[slot]) return; - if(this.dojoTask[slot] != null) { - this.dojoTask[slot].cancel(false); - this.dojoTask[slot] = null; + lock.lock(); + try { + if(this.dojoTask[slot] != null) { + this.dojoTask[slot].cancel(false); + this.dojoTask[slot] = null; + } + } finally { + lock.unlock(); } freeDojoSlot(slot, party); diff --git a/src/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/net/server/channel/handlers/AbstractDealDamageHandler.java index d0455c3c99..027f0e8691 100644 --- a/src/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -56,7 +56,6 @@ import client.autoban.AutobanFactory; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; import constants.game.GameConstants; -import constants.net.ServerConstants; import constants.skills.Aran; import constants.skills.Assassin; import constants.skills.Bandit; @@ -102,6 +101,7 @@ import constants.skills.SuperGM; import constants.skills.ThunderBreaker; import constants.skills.WhiteKnight; import constants.skills.WindArcher; +import net.server.PlayerBuffValueHolder; import scripting.AbstractPlayerInteraction; public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandler { @@ -166,14 +166,17 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl if (player.isAlive()) { if(attack.skill == Aran.BODY_PRESSURE || attack.skill == Marauder.ENERGY_CHARGE || attack.skill == ThunderBreaker.ENERGY_CHARGE) { // thanks IxianMace for noticing Energy Charge skills refreshing on touch, leading to misleading buff applies // prevent touch dmg skills refreshing + } else if(attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == WindArcher.FINAL_ATTACK) { + // prevent cygnus FA refreshing + mobCount = 15; } else if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb attackEffect.applyTo(player, new Point(attack.position.x, attack.position.y)); } else { attackEffect.applyTo(player); - if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD - || attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM || attack.skill == WindArcher.FINAL_ATTACK - || attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) { + if (attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD + || attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM + || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) { mobCount = 15;//:( } else if (attack.skill == Aran.HIDDEN_FULL_DOUBLE || attack.skill == Aran.HIDDEN_FULL_TRIPLE || attack.skill == Aran.HIDDEN_OVER_DOUBLE || attack.skill == Aran.HIDDEN_OVER_TRIPLE) { @@ -753,6 +756,17 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl calcDmgMax *= (100 + ceffect.getDamage()) / 100; } + int bonusDmgBuff = 100; + for (PlayerBuffValueHolder pbvh : chr.getAllBuffs()) { + int bonusDmg = pbvh.effect.getDamage() - 100; + bonusDmgBuff += bonusDmg; + } + + if (bonusDmgBuff != 100) { + float dmgBuff = bonusDmgBuff / 100.0f; + calcDmgMax = (long) Math.ceil(calcDmgMax * dmgBuff); + } + if(chr.getMapId() >= 914000000 && chr.getMapId() <= 914000500) { calcDmgMax += 80000; // Aran Tutorial. } @@ -789,7 +803,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl if(chr.getBuffEffect(MapleBuffStat.WK_CHARGE) != null) { // Charge, so now we need to check elemental effectiveness int sourceID = chr.getBuffSource(MapleBuffStat.WK_CHARGE); - int level = chr.getBuffedValue(MapleBuffStat.WK_CHARGE); + int level = chr.getBuffedValue(MapleBuffStat.WK_CHARGE); if(monster != null) { if(sourceID == WhiteKnight.BW_FIRE_CHARGE || sourceID == WhiteKnight.SWORD_FIRE_CHARGE) { if(monster.getStats().getEffectiveness(Element.FIRE) == ElementalEffectiveness.WEAK) { @@ -880,7 +894,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl long maxWithCrit = hitDmgMax; if(canCrit) // They can crit, so up the max. maxWithCrit *= 2; - + // Warn if the damage is over 1.5x what we calculated above. if(damage > maxWithCrit * 1.5) { AutobanFactory.DAMAGE_HACK.alert(chr, "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")"); diff --git a/src/net/server/channel/handlers/CloseRangeDamageHandler.java b/src/net/server/channel/handlers/CloseRangeDamageHandler.java index d7792beec8..621ce02426 100644 --- a/src/net/server/channel/handlers/CloseRangeDamageHandler.java +++ b/src/net/server/channel/handlers/CloseRangeDamageHandler.java @@ -68,7 +68,7 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { if (chr.getDojoEnergy() < 10000 && (attack.skill == 1009 || attack.skill == 10001009 || attack.skill == 20001009)) // PE hacking or maybe just lagging return; - if (chr.getMap().isDojoMap() && attack.numAttacked > 0) { + if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) { chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK); c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy())); } diff --git a/src/net/server/channel/handlers/MagicDamageHandler.java b/src/net/server/channel/handlers/MagicDamageHandler.java index fa82dbfd1c..b1f9c56303 100644 --- a/src/net/server/channel/handlers/MagicDamageHandler.java +++ b/src/net/server/channel/handlers/MagicDamageHandler.java @@ -27,6 +27,7 @@ import client.MapleClient; import client.Skill; import client.SkillFactory; import config.YamlConfig; +import constants.game.GameConstants; import constants.skills.Bishop; import constants.skills.Evan; import constants.skills.FPArchMage; @@ -56,7 +57,7 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler { } } - if (chr.getMap().isDojoMap() && attack.numAttacked > 0) { + if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) { chr.setDojoEnergy(chr.getDojoEnergy() + + YamlConfig.config.server.DOJO_ENERGY_ATK); c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy())); } diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java index 4a25fd4f1c..c9137b68b3 100644 --- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -130,7 +130,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { String remoteHwid; if (player == null) { - remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session); + remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(session); if (remoteHwid == null) { c.disconnect(true, false); return; diff --git a/src/net/server/channel/handlers/QuickslotKeyMappedModifiedHandler.java b/src/net/server/channel/handlers/QuickslotKeyMappedModifiedHandler.java index 2e1881c0bb..74f3f5d64f 100644 --- a/src/net/server/channel/handlers/QuickslotKeyMappedModifiedHandler.java +++ b/src/net/server/channel/handlers/QuickslotKeyMappedModifiedHandler.java @@ -29,6 +29,6 @@ public class QuickslotKeyMappedModifiedHandler extends AbstractMaplePacketHandle aQuickslotKeyMapped[i] = (byte) slea.readInt(); } - c.getPlayer().m_pQuickslotKeyMapped = new MapleQuickslotBinding(aQuickslotKeyMapped); + c.getPlayer().changeQuickslotKeybinding(aQuickslotKeyMapped); } } diff --git a/src/net/server/channel/handlers/RangedAttackHandler.java b/src/net/server/channel/handlers/RangedAttackHandler.java index f2d33047f4..5b7f7e19cb 100644 --- a/src/net/server/channel/handlers/RangedAttackHandler.java +++ b/src/net/server/channel/handlers/RangedAttackHandler.java @@ -32,6 +32,7 @@ import client.inventory.MapleInventoryType; import client.inventory.MapleWeaponType; import client.inventory.manipulator.MapleInventoryManipulator; import config.YamlConfig; +import constants.game.GameConstants; import constants.inventory.ItemConstants; import constants.skills.Aran; import constants.skills.Buccaneer; @@ -69,7 +70,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } } - if (chr.getMap().isDojoMap() && attack.numAttacked > 0) { + if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) { chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK); c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy())); } @@ -104,7 +105,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } short slot = -1; int projectile = 0; - byte bulletCount = 1; + short bulletCount = 1; MapleStatEffect effect = null; if (attack.skill != 0) { effect = attack.getAttackEffect(chr, null); @@ -168,7 +169,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { boolean shadowClaw = chr.getBuffedValue(MapleBuffStat.SHADOW_CLAW) != null; if (projectile != 0) { if (!soulArrow && !shadowClaw && attack.skill != 11101004 && attack.skill != 15111007 && attack.skill != 14101006) { - byte bulletConsume = bulletCount; + short bulletConsume = bulletCount; if (effect != null && effect.getBulletConsume() != 0) { bulletConsume = (byte) (effect.getBulletConsume() * (hasShadowPartner ? 2 : 1)); diff --git a/src/net/server/channel/handlers/TakeDamageHandler.java b/src/net/server/channel/handlers/TakeDamageHandler.java index ee1c30db75..488937d6c1 100644 --- a/src/net/server/channel/handlers/TakeDamageHandler.java +++ b/src/net/server/channel/handlers/TakeDamageHandler.java @@ -197,7 +197,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler { } //in dojo player cannot use pot, so deadly attacks should be turned off as well - if(is_deadly && chr.getMap().isDojoMap() && !YamlConfig.config.server.USE_DEADLY_DOJO) { + if(is_deadly && GameConstants.isDojo(chr.getMap().getId()) && !YamlConfig.config.server.USE_DEADLY_DOJO) { damage = 0; mpattack = 0; } diff --git a/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java b/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java index 421b4aec3d..9133a39796 100644 --- a/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java +++ b/src/net/server/coordinator/matchchecker/MapleMatchCheckerCoordinator.java @@ -319,10 +319,14 @@ public class MapleMatchCheckerCoordinator { } } - private void acceptMatchElement(MapleMatchCheckingElement mmce, int cid) { + private boolean acceptMatchElement(MapleMatchCheckingElement mmce, int cid) { if (mmce.acceptEntry(cid)) { unpoolMatchPlayer(cid); disposeMatchElement(mmce); + + return true; + } else { + return false; } } @@ -355,7 +359,11 @@ public class MapleMatchCheckerCoordinator { mmce = null; } else { if (accept) { - acceptMatchElement(mmce, cid); + if (!acceptMatchElement(mmce, cid)) { + mmce = null; + } + + break; // thanks Rohenn for noticing loop scenario here } else { denyMatchElement(mmce, cid); matchEntries.remove(cid); diff --git a/src/net/server/coordinator/session/MapleSessionCoordinator.java b/src/net/server/coordinator/session/MapleSessionCoordinator.java index cf922b2c58..95fc2e6289 100644 --- a/src/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/net/server/coordinator/session/MapleSessionCoordinator.java @@ -547,6 +547,11 @@ public class MapleSessionCoordinator { // session.removeAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); No real need for removing String property on closed sessions } + public String pickLoginSessionHwid(IoSession session) { + String remoteHost = getSessionRemoteAddress(session); + return cachedHostHwids.remove(remoteHost); // thanks BHB, resinate for noticing players from same network not being able to login + } + public String getGameSessionHwid(IoSession session) { String remoteHost = getSessionRemoteHost(session); return cachedHostHwids.get(remoteHost); diff --git a/src/net/server/handlers/login/LoginPasswordHandler.java b/src/net/server/handlers/login/LoginPasswordHandler.java index 45beacc73c..248699e72f 100644 --- a/src/net/server/handlers/login/LoginPasswordHandler.java +++ b/src/net/server/handlers/login/LoginPasswordHandler.java @@ -88,8 +88,9 @@ public final class LoginPasswordHandler implements MaplePacketHandler { slea.skip(6); // localhost masked the initial part with zeroes... byte[] hwidNibbles = slea.read(4); - int loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles)); - + String nibbleHwid = HexTool.toCompressedString(hwidNibbles); + int loginok = c.login(login, pwd, nibbleHwid); + Connection con = null; PreparedStatement ps = null; @@ -112,7 +113,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler { e.printStackTrace(); } finally { disposeSql(con, ps); - loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles)); + loginok = c.login(login, pwd, nibbleHwid); } } diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index b22c6837da..6d5181edd9 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -260,10 +260,15 @@ public class World { } } - public void addChannel(Channel channel) { + public boolean addChannel(Channel channel) { chnWLock.lock(); try { - channels.add(channel); + if (channel.getId() == channels.size() + 1) { + channels.add(channel); + return true; + } else { + return false; + } } finally { chnWLock.unlock(); } diff --git a/src/scripting/npc/NPCConversationManager.java b/src/scripting/npc/NPCConversationManager.java index 2df36ef2a5..52680715d6 100644 --- a/src/scripting/npc/NPCConversationManager.java +++ b/src/scripting/npc/NPCConversationManager.java @@ -588,7 +588,10 @@ public class NPCConversationManager extends AbstractPlayerInteraction { } public Object[] getAvailableSkillBooks() { - return MapleItemInformationProvider.getInstance().usableSkillBooks(this.getPlayer()).toArray(); + List ret = MapleItemInformationProvider.getInstance().usableSkillBooks(this.getPlayer()); + ret.addAll(MapleSkillbookInformationProvider.getInstance().getTeachableSkills(this.getPlayer())); + + return ret.toArray(); } public Object[] getNamesWhoDropsItem(Integer itemId) { @@ -597,7 +600,19 @@ public class NPCConversationManager extends AbstractPlayerInteraction { public String getSkillBookInfo(int itemid) { SkillBookEntry sbe = MapleSkillbookInformationProvider.getInstance().getSkillbookAvailability(itemid); - return sbe != SkillBookEntry.UNAVAILABLE ? " Obtainable through #rquestline#k." : ""; + switch (sbe) { + case UNAVAILABLE: + return ""; + + case QUEST_BOOK: + return " Obtainable through #rquestline#k (collecting book)."; + + case QUEST_REWARD: + return " Obtainable through #rquestline#k (quest reward)."; + + default: + return " Obtainable through #rquestline#k."; + } } // By Drago/Dragohe4rt CPQ + WED diff --git a/src/server/MakerItemFactory.java b/src/server/MakerItemFactory.java index 25591b71f9..200b0eebe0 100644 --- a/src/server/MakerItemFactory.java +++ b/src/server/MakerItemFactory.java @@ -38,8 +38,8 @@ public class MakerItemFactory { public static MakerItemCreateEntry getItemCreateEntry(int toCreate, int stimulantid, Map reagentids) { MakerItemCreateEntry makerEntry = ii.getMakerItemEntry(toCreate); - if(makerEntry == null) { - return null; + if(makerEntry.isInvalid()) { + return makerEntry; } // THEY DECIDED FOR SOME BIZARRE PATTERN ON THE FEE THING, ALMOST RANDOMIZED. @@ -206,5 +206,9 @@ public class MakerItemFactory { reqCost = (int) (cost / 1000); reqCost *= 1000; } + + public boolean isInvalid() { // thanks Rohenn, Wh1SK3Y for noticing some items not getting checked properly + return reqLevel < 0; + } } } diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java index d0713ed1b6..235016b174 100644 --- a/src/server/MapleItemInformationProvider.java +++ b/src/server/MapleItemInformationProvider.java @@ -1981,10 +1981,10 @@ public class MapleItemInformationProvider { PreparedStatement ps = con.prepareStatement("SELECT req_level, req_maker_level, req_meso, quantity FROM makercreatedata WHERE itemid = ?"); ps.setInt(1, toCreate); ResultSet rs = ps.executeQuery(); - int reqLevel = 0; - int reqMakerLevel = 0; - int cost = 0; - int toGive = 0; + int reqLevel = -1; + int reqMakerLevel = -1; + int cost = -1; + int toGive = -1; if (rs.next()) { reqLevel = rs.getInt("req_level"); reqMakerLevel = rs.getInt("req_maker_level"); diff --git a/src/server/MapleSkillbookInformationProvider.java b/src/server/MapleSkillbookInformationProvider.java index 57e9d8309e..2b2c2114ba 100644 --- a/src/server/MapleSkillbookInformationProvider.java +++ b/src/server/MapleSkillbookInformationProvider.java @@ -35,7 +35,9 @@ import java.util.Scanner; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import client.MapleCharacter; import provider.MapleData; +import provider.MapleDataProvider; import provider.MapleDataProviderFactory; import provider.MapleDataTool; import tools.DatabaseConnection; @@ -57,45 +59,106 @@ public class MapleSkillbookInformationProvider { public enum SkillBookEntry { UNAVAILABLE, QUEST, + QUEST_BOOK, + QUEST_REWARD, REACTOR, SCRIPT } - static String host = "jdbc:mysql://localhost:3306/heavenms"; - static String driver = "com.mysql.jdbc.Driver"; - static String username = "root"; - static String password = ""; + private static String host = "jdbc:mysql://localhost:3306/heavenms"; + private static String driver = "com.mysql.jdbc.Driver"; + private static String username = "root"; + private static String password = ""; - static String rootDirectory = "."; + private static String rootDirectory = "."; - static int skillbookMinItemid = 2280000; - static int skillbookMaxItemid = 2300000; // exclusively + private static int skillbookMinItemid = 2280000; + private static int skillbookMaxItemid = 2300000; // exclusively + + private static Set questSkills = new HashSet<>(); static { loadSkillbooks(); } - public static boolean isSkillBook(int itemid) { + private static boolean is4thJobSkill(int itemid) { + return itemid / 10000 % 10 == 2; + } + + private static boolean isSkillBook(int itemid) { return itemid >= skillbookMinItemid && itemid < skillbookMaxItemid; } + private static boolean isQuestBook(int itemid) { + return itemid >= 4001107 && itemid <= 4001114 || itemid >= 4161015 && itemid <= 4161023; + } + + private static int fetchQuestbook(MapleData checkData, String quest) { + MapleData questStartData = checkData.getChildByPath(quest).getChildByPath("0"); + + MapleData startReqItemData = questStartData.getChildByPath("item"); + if (startReqItemData != null) { + for (MapleData itemData : startReqItemData.getChildren()) { + int itemid = MapleDataTool.getInt("id", itemData, 0); + if (isQuestBook(itemid)) { + return itemid; + } + } + } + + MapleData startReqQuestData = questStartData.getChildByPath("quest"); + if (startReqQuestData != null) { + Set reqQuests = new HashSet<>(); + + for (MapleData questStatusData : startReqQuestData.getChildren()) { + int reqQuest = MapleDataTool.getInt("id", questStatusData, 0); + if (reqQuest > 0) { + reqQuests.add(reqQuest); + } + } + + for (Integer reqQuest : reqQuests) { + int book = fetchQuestbook(checkData, Integer.toString(reqQuest)); + if (book > -1) { + return book; + } + } + } + + return -1; + } + private static void fetchSkillbooksFromQuests() { - MapleData actData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz")).getData("Act.img"); + MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz")); + MapleData actData = questDataProvider.getData("Act.img"); + MapleData checkData = questDataProvider.getData("Check.img"); for (MapleData questData : actData.getChildren()) { for (MapleData questStatusData : questData.getChildren()) { for (MapleData questNodeData : questStatusData.getChildren()) { - if (questNodeData.getName().contentEquals("item")) { + String actNodeName = questNodeData.getName(); + if (actNodeName.contentEquals("item")) { for (MapleData questItemData : questNodeData.getChildren()) { int itemid = MapleDataTool.getInt("id", questItemData, 0); int itemcount = MapleDataTool.getInt("count", questItemData, 0); if (isSkillBook(itemid) && itemcount > 0) { - foundSkillbooks.put(itemid, SkillBookEntry.QUEST); + int questbook = fetchQuestbook(checkData, questData.getName()); + if (questbook < 0) { + foundSkillbooks.put(itemid, SkillBookEntry.QUEST); + } else { + foundSkillbooks.put(itemid, SkillBookEntry.QUEST_BOOK); + } + } + } + } else if (actNodeName.contentEquals("skill")) { + for (MapleData questSkillData : questNodeData.getChildren()) { + int skillid = MapleDataTool.getInt("id", questSkillData, 0); + if (is4thJobSkill(skillid)) { + // negative itemids are skill rewards + foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD); } } - - break; } } } @@ -212,4 +275,23 @@ public class MapleSkillbookInformationProvider { return sbe != null ? sbe : SkillBookEntry.UNAVAILABLE; } + public List getTeachableSkills(MapleCharacter chr) { + List list = new ArrayList<>(); + + for (Integer book : foundSkillbooks.keySet()) { + if (book >= 0) { + continue; + } + + int skillid = -book; + if (skillid / 10000 == chr.getJob().getId()) { + if (chr.getMasterLevel(skillid) == 0) { + list.add(-skillid); + } + } + } + + return list; + } + } diff --git a/src/server/MapleStatEffect.java b/src/server/MapleStatEffect.java index d16363d74b..5fa69ad6c9 100644 --- a/src/server/MapleStatEffect.java +++ b/src/server/MapleStatEffect.java @@ -146,7 +146,7 @@ public class MapleStatEffect { private int itemCon, itemConNo; private int damage, attackCount, fixdamage; private Point lt, rb; - private byte bulletCount, bulletConsume; + private short bulletCount, bulletConsume; private byte mapProtection; private CardItemupStats cardStats; @@ -484,8 +484,8 @@ public class MapleStatEffect { ret.damage = MapleDataTool.getIntConvert("damage", source, 100); ret.fixdamage = MapleDataTool.getIntConvert("fixdamage", source, -1); ret.attackCount = MapleDataTool.getIntConvert("attackCount", source, 1); - ret.bulletCount = (byte) MapleDataTool.getIntConvert("bulletCount", source, 1); - ret.bulletConsume = (byte) MapleDataTool.getIntConvert("bulletConsume", source, 0); + ret.bulletCount = (short) MapleDataTool.getIntConvert("bulletCount", source, 1); + ret.bulletConsume = (short) MapleDataTool.getIntConvert("bulletConsume", source, 0); ret.moneyCon = MapleDataTool.getIntConvert("moneyCon", source, 0); ret.itemCon = MapleDataTool.getInt("itemCon", source, 0); ret.itemConNo = MapleDataTool.getInt("itemConNo", source, 0); @@ -961,6 +961,7 @@ public class MapleStatEffect { } else if (isCureAllAbnormalStatus()) { applyto.dispelDebuff(MapleDisease.SEDUCE); applyto.dispelDebuff(MapleDisease.ZOMBIFY); + applyto.dispelDebuff(MapleDisease.CONFUSE); applyto.dispelDebuffs(); } else if (isComboReset()) { applyto.setCombo((short) 0); @@ -1008,6 +1009,8 @@ public class MapleStatEffect { } } if (isShadowClaw()) { + short projectileConsume = this.getBulletConsume(); // noticed by shavit + MapleInventory use = applyto.getInventory(MapleInventoryType.USE); use.lockInventory(); try { @@ -1015,7 +1018,7 @@ public class MapleStatEffect { for (int i = 1; i <= use.getSlotLimit(); i++) { // impose order... Item item = use.getItem((short) i); if (item != null) { - if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= 200) { + if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= projectileConsume) { projectile = item; break; } @@ -1024,7 +1027,7 @@ public class MapleStatEffect { if (projectile == null) { return false; } else { - MapleInventoryManipulator.removeFromSlot(applyto.getClient(), MapleInventoryType.USE, projectile.getPosition(), (short) 200, false, true); + MapleInventoryManipulator.removeFromSlot(applyto.getClient(), MapleInventoryType.USE, projectile.getPosition(), (short) projectileConsume, false, true); } } finally { use.unlockInventory(); @@ -1938,11 +1941,11 @@ public class MapleStatEffect { return fixdamage; } - public byte getBulletCount() { + public short getBulletCount() { return bulletCount; } - public byte getBulletConsume() { + public short getBulletConsume() { return bulletConsume; } diff --git a/src/server/life/MobSkill.java b/src/server/life/MobSkill.java index a5cd921fd5..57eb5db6a5 100644 --- a/src/server/life/MobSkill.java +++ b/src/server/life/MobSkill.java @@ -180,7 +180,7 @@ public class MobSkill { break; case 127: if (lt != null && rb != null && skill) { - for (MapleCharacter character : getPlayersInRange(monster, player)) { + for (MapleCharacter character : getPlayersInRange(monster)) { character.dispel(); } } else { @@ -192,7 +192,7 @@ public class MobSkill { break; case 129: // Banish if (lt != null && rb != null && skill) { - for (MapleCharacter chr : getPlayersInRange(monster, player)) { + for (MapleCharacter chr : getPlayersInRange(monster)) { banishPlayers.add(chr); } } else { @@ -200,7 +200,7 @@ public class MobSkill { } break; case 131: // Mist - monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), monster, this), x * 100, false, false, false); + monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition()), monster, this), x * 100, false, false, false); break; case 132: disease = MapleDisease.CONFUSE; @@ -248,7 +248,7 @@ public class MobSkill { int skillLimit = this.getLimit(); MapleMap map = monster.getMap(); - if (map.isDojoMap()) { // spawns in dojo should be unlimited + if (GameConstants.isDojo(map.getId())) { // spawns in dojo should be unlimited skillLimit = Integer.MAX_VALUE; } @@ -333,7 +333,7 @@ public class MobSkill { if (disease != null) { if (lt != null && rb != null && skill) { int i = 0; - for (MapleCharacter character : getPlayersInRange(monster, player)) { + for (MapleCharacter character : getPlayersInRange(monster)) { if (!character.hasActiveBuff(2321005)) { // holy shield if (disease.equals(MapleDisease.SEDUCE)) { if (i < 10) { @@ -351,8 +351,8 @@ public class MobSkill { } } - private List getPlayersInRange(MapleMonster monster, MapleCharacter player) { - return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), Collections.singletonList(player)); + private List getPlayersInRange(MapleMonster monster) { + return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition())); } public int getSkillId() { @@ -411,15 +411,14 @@ public class MobSkill { return prop == 1.0 || Math.random() < prop; } - private Rectangle calculateBoundingBox(Point posFrom, boolean facingLeft) { - int multiplier = facingLeft ? 1 : -1; - Point mylt = new Point(lt.x * multiplier + posFrom.x, lt.y + posFrom.y); - Point myrb = new Point(rb.x * multiplier + posFrom.x, rb.y + posFrom.y); + private Rectangle calculateBoundingBox(Point posFrom) { + Point mylt = new Point(lt.x + posFrom.x, lt.y + posFrom.y); + Point myrb = new Point(rb.x + posFrom.x, rb.y + posFrom.y); Rectangle bounds = new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y); return bounds; } private List getObjectsInRange(MapleMonster monster, MapleMapObjectType objectType) { - return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), Collections.singletonList(objectType)); + return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition()), Collections.singletonList(objectType)); } } diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 32b3d0dab9..95aa1b4390 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -758,9 +758,11 @@ public class MapleMap { final List dropEntry = new ArrayList<>(); final List visibleQuestEntry = new ArrayList<>(); final List otherQuestEntry = new ArrayList<>(); - sortDropEntries(YamlConfig.config.server.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr); - if (dropEntry.isEmpty() && visibleQuestEntry.isEmpty()) { // thanks resinate + List lootEntry = YamlConfig.config.server.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId()); + sortDropEntries(lootEntry, dropEntry, visibleQuestEntry, otherQuestEntry, chr); // thanks Articuno, Limit, Rohenn for noticing quest loots not showing up in only-quest item drops scenario + + if (lootEntry.isEmpty()) { // thanks resinate return; } @@ -1309,15 +1311,13 @@ public class MapleMap { return pchars; } - public List getPlayersInRange(Rectangle box, List targets) { + public List getPlayersInRange(Rectangle box) { List character = new LinkedList<>(); chrRLock.lock(); try { - for (MapleCharacter chr : targets) { - if (characters.contains(chr)) { - if (box.contains(chr.getPosition())) { - character.add(chr); - } + for (MapleCharacter chr : characters) { + if (box.contains(chr.getPosition())) { + character.add(chr); } } } finally { @@ -4173,14 +4173,6 @@ public class MapleMap { this.setDocked(state); } - public boolean isDojoMap() { - return mapid >= 925020000 && mapid < 925040000; - } - - public boolean isDojoFightMap() { - return isDojoMap() && (((mapid / 100) % 100) % 6) > 0; - } - public boolean isHorntailDefeated() { // all parts of dead horntail can be found here? for(int i = 8810010; i <= 8810017; i++) { if (getMonsterById(i) == null) { diff --git a/src/server/maps/MaplePlayerShop.java b/src/server/maps/MaplePlayerShop.java index c895ae998a..f541c3a758 100644 --- a/src/server/maps/MaplePlayerShop.java +++ b/src/server/maps/MaplePlayerShop.java @@ -272,13 +272,13 @@ public class MaplePlayerShop extends AbstractMapleMapObject { int price = (int) Math.min((float)pItem.getPrice() * quantity, Integer.MAX_VALUE); if (c.getPlayer().getMeso() >= price) { + if (!owner.canHoldMeso(price)) { // thanks Rohenn for noticing owner hold check misplaced + c.getPlayer().dropMessage(1, "Transaction failed since the shop owner can't hold any more mesos."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + if (canBuy(c, newItem)) { - if (!owner.canHoldMeso(price)) { - owner.dropMessage(1, "Transaction failed since the shop owner can't hold any more mesos."); - c.announce(MaplePacketCreator.enableActions()); - return false; - } - c.getPlayer().gainMeso(-price, false); price -= MapleTrade.getFee(price); // thanks BHB for pointing out trade fees not applying here owner.gainMeso(price, true); diff --git a/src/server/quest/actions/ItemAction.java b/src/server/quest/actions/ItemAction.java index 15b69c64fc..a76384904b 100644 --- a/src/server/quest/actions/ItemAction.java +++ b/src/server/quest/actions/ItemAction.java @@ -61,6 +61,7 @@ public class ItemAction extends MapleQuestAction { for (MapleData iEntry : data.getChildren()) { int id = MapleDataTool.getInt(iEntry.getChildByPath("id")); int count = MapleDataTool.getInt(iEntry.getChildByPath("count"), 1); + int period = MapleDataTool.getInt(iEntry.getChildByPath("period"), 0); Integer prop = null; MapleData propData = iEntry.getChildByPath("prop"); @@ -75,7 +76,7 @@ public class ItemAction extends MapleQuestAction { if (iEntry.getChildByPath("job") != null) job = MapleDataTool.getInt(iEntry.getChildByPath("job")); - items.add(new ItemData(Integer.parseInt(iEntry.getName()), id, count, prop, job, gender)); + items.add(new ItemData(Integer.parseInt(iEntry.getName()), id, count, prop, job, gender, period)); } Collections.sort(items, new Comparator() @@ -90,8 +91,8 @@ public class ItemAction extends MapleQuestAction { @Override public void run(MapleCharacter chr, Integer extSelection) { - List> takeItem = new LinkedList<>(); - List> giveItem = new LinkedList<>(); + List takeItem = new LinkedList<>(); + List giveItem = new LinkedList<>(); int props = 0, rndProps = 0, accProps = 0; for(ItemData item : items) { @@ -125,34 +126,38 @@ public class ItemAction extends MapleQuestAction { } if(iEntry.getCount() < 0) { // Remove Item - takeItem.add(new Pair<>(iEntry.getId(), iEntry.getCount())); + takeItem.add(iEntry); } else { // Give Item - giveItem.add(new Pair<>(iEntry.getId(), iEntry.getCount())); + giveItem.add(iEntry); } } // must take all needed items before giving others - for(Pair iPair: takeItem) { - MapleInventoryType type = ItemConstants.getInventoryType(iPair.getLeft()); - int quantity = iPair.getRight() * -1; // Invert + for(ItemData iEntry: takeItem) { + int itemid = iEntry.getId(), count = iEntry.getCount(); + + MapleInventoryType type = ItemConstants.getInventoryType(itemid); + int quantity = count * -1; // Invert if(type.equals(MapleInventoryType.EQUIP)) { - if(chr.getInventory(type).countById(iPair.getLeft()) < quantity) { + if(chr.getInventory(type).countById(itemid) < quantity) { // Not enough in the equip inventoty, so check Equipped... - if(chr.getInventory(MapleInventoryType.EQUIPPED).countById(iPair.getLeft()) > quantity) { + if(chr.getInventory(MapleInventoryType.EQUIPPED).countById(itemid) > quantity) { // Found it equipped, so change the type to equipped. type = MapleInventoryType.EQUIPPED; } } } - MapleInventoryManipulator.removeById(chr.getClient(), type, iPair.getLeft(), quantity, true, false); - chr.announce(MaplePacketCreator.getShowItemGain(iPair.getLeft(), (short) iPair.getRight().shortValue(), true)); + MapleInventoryManipulator.removeById(chr.getClient(), type, itemid, quantity, true, false); + chr.announce(MaplePacketCreator.getShowItemGain(itemid, (short) count, true)); } - for(Pair iPair: giveItem) { - MapleInventoryManipulator.addById(chr.getClient(), iPair.getLeft(), (short) iPair.getRight().shortValue(), "", -1); - chr.announce(MaplePacketCreator.getShowItemGain(iPair.getLeft(), (short) iPair.getRight().shortValue(), true)); + for(ItemData iEntry: giveItem) { + int itemid = iEntry.getId(), count = iEntry.getCount(), period = iEntry.getPeriod(); // thanks Vcoc for noticing quest milestone item not getting removed from inventory after a while + + MapleInventoryManipulator.addById(chr.getClient(), itemid, (short) count, "", -1, period > 0 ? (System.currentTimeMillis() + period * 60 * 1000) : -1); + chr.announce(MaplePacketCreator.getShowItemGain(itemid, (short) count, true)); } } @@ -320,16 +325,17 @@ public class ItemAction extends MapleQuestAction { } private class ItemData { - private final int map, id, count, job, gender; + private final int map, id, count, job, gender, period; private final Integer prop; - public ItemData(int map, int id, int count, Integer prop, int job, int gender) { + public ItemData(int map, int id, int count, Integer prop, int job, int gender, int period) { this.map = map; this.id = id; this.count = count; this.prop = prop; this.job = job; this.gender = gender; + this.period = period; } public int getId() { @@ -351,5 +357,9 @@ public class ItemAction extends MapleQuestAction { public int getGender() { return gender; } + + public int getPeriod() { + return period; + } } } diff --git a/wz/Item.wz/Consume/0202.img.xml b/wz/Item.wz/Consume/0202.img.xml index 6db7fddb1f..a455239cd4 100644 --- a/wz/Item.wz/Consume/0202.img.xml +++ b/wz/Item.wz/Consume/0202.img.xml @@ -4695,10 +4695,10 @@ - + - +