From 5ee0cd1c98002d81b343212b85ede7b97e8a45ce Mon Sep 17 00:00:00 2001 From: ronancpl Date: Sat, 10 Nov 2018 17:48:35 -0200 Subject: [PATCH] Using Java ThreadPool + Mob Skills & Event Instance patch + Eqp Merge Server source now uses Java ThreadPool, recycling used thread resources for next uses. Added Grenade visual effect for other players. Implemented an attempt towards unsynced mob behavior, where reportedly players were able to pin same mob in different sections of the map. Solved several deadlock issues, mostly regarding character synchronized methods, event instance scripts and player/item vision-unvision. Solved an issue where mobs would not cast some skills of it's skillset. Frequent behavior on low-leveled mobs. Fixed a bug on 2nd Maker quest where players could complete it by merely disassembling an equipment. New custom mechanic: equipment merge. Similarly to the Bazaar NPC, every equipment after the selected one is used up, and a fraction of their stat amounts are used as stat gains on the currently equipped items. If restrictions are enabled, players must be high-leveled and Maker lv3 to use it. Skill Storm Break no longer uses up arrows. Added a server flag to allow access for all Aran job skills from the beginning. Implemented Battleship and Super Transformation questline scripts. Fixed a desynchronization within pet position and cash inventory position, that could potentially lead to some inventory issues until relogin. Improved timestamp handling in some handler classes. Spam detection is entirely a server-side matter, hence removed usage of client-sided timestamp content. Refactored some pet response packets, improving some of their behaviors. Fixed some quest issues: Maker lv1 and Omega Sector meteorite one. --- README.md | 6 +- docs/feature_list.md | 4 + .../userstances.txt => docs/moveactions.txt | 0 docs/mychanges_ptbr.txt | 49 +- docs/npcmarkups.txt | 28 + scripts/event/4jship.js | 138 + scripts/event/4jsuper.js | 140 + scripts/npc/1032000.js | 34 +- scripts/npc/1052014.js | 2 +- scripts/npc/1090000.js | 52 +- scripts/npc/1100003.js | 25 +- scripts/npc/1100006.js | 2 +- scripts/npc/1101001.js | 10 +- scripts/npc/2042000.js | 12 +- scripts/npc/2050014.js | 12 +- scripts/npc/2050015.js | 12 +- scripts/npc/2050016.js | 12 +- scripts/npc/2050017.js | 12 +- scripts/npc/2050018.js | 12 +- scripts/npc/2050019.js | 12 +- scripts/npc/2091005.js | 9 +- scripts/npc/2101013.js | 4 +- scripts/npc/9000017.js | 6 + scripts/npc/9000036.js | 18 +- scripts/npc/9000040.js | 79 +- scripts/npc/9000041.js | 6 + scripts/npc/9201056.js | 17 +- scripts/npc/9977777.js | 4 + scripts/portal/enterDisguise1.js | 2 +- scripts/portal/enterDisguise2.js | 2 +- scripts/portal/enterDisguise3.js | 2 +- scripts/portal/enterDisguise4.js | 2 +- scripts/portal/enterDisguise5.js | 2 +- scripts/portal/s4ship_out.js | 32 + scripts/portal/s4super_out.js | 32 + scripts/quest/21101.js | 7 +- scripts/quest/21201.js | 8 + scripts/quest/21302.js | 5 + scripts/quest/3239.js | 61 +- scripts/quest/6030.js | 2 +- sql/db_drops.sql | 15 +- src/client/MapleCharacter.java | 410 +- src/client/MapleClient.java | 29 +- src/client/autoban/AutobanManager.java | 7 +- .../command/commands/gm0/TimeCommand.java | 2 +- .../command/commands/gm2/SummonCommand.java | 7 +- .../command/commands/gm5/DebugCommand.java | 3 +- .../commands/gm6/ServerAddChannelCommand.java | 5 +- .../commands/gm6/ServerAddWorldCommand.java | 5 +- .../gm6/ServerRemoveChannelCommand.java | 5 +- .../gm6/ServerRemoveWorldCommand.java | 5 +- src/client/inventory/Equip.java | 116 +- src/client/inventory/Item.java | 1 + src/client/inventory/MaplePet.java | 2 +- .../MapleInventoryManipulator.java | 6 + src/client/processor/AssignAPProcessor.java | 4 +- src/client/processor/AssignSPProcessor.java | 4 +- src/client/processor/MakerProcessor.java | 9 +- src/constants/ItemConstants.java | 1 + src/constants/ServerConstants.java | 10 +- src/constants/skills/WindArcher.java | 1 + src/net/PacketProcessor.java | 1 + src/net/opcodes/RecvOpcode.java | 3 +- src/net/opcodes/SendOpcode.java | 1 + src/net/server/PlayerStorage.java | 2 +- src/net/server/Server.java | 39 +- src/net/server/audit/ThreadTracker.java | 4 +- .../audit/locks/active/TrackerReadLock.java | 2 +- .../locks/active/TrackerReentrantLock.java | 2 +- .../audit/locks/active/TrackerWriteLock.java | 2 +- .../audit/locks/empty/EmptyReadLock.java | 2 +- .../audit/locks/empty/EmptyReentrantLock.java | 2 +- .../audit/locks/empty/EmptyWriteLock.java | 2 +- .../handlers/AbstractDealDamageHandler.java | 5 +- .../channel/handlers/AutoAggroHandler.java | 27 +- .../handlers/ChangeChannelHandler.java | 4 +- .../handlers/GrenadeEffectHandler.java | 57 + .../channel/handlers/HealOvertimeHandler.java | 6 +- .../handlers/InventoryMergeHandler.java | 4 +- .../handlers/InventorySortHandler.java | 6 +- .../channel/handlers/MoveLifeHandler.java | 64 +- .../channel/handlers/PetCommandHandler.java | 3 +- .../channel/handlers/PetFoodHandler.java | 6 +- .../channel/handlers/RangedAttackHandler.java | 6 +- .../channel/handlers/ScriptedItemHandler.java | 2 +- .../channel/handlers/SpecialMoveHandler.java | 17 +- .../channel/handlers/TouchReactorHandler.java | 2 + .../channel/handlers/UseCatchItemHandler.java | 4 +- .../handlers/UseOwlOfMinervaHandler.java | 1 - src/net/server/world/World.java | 4 +- src/scripting/AbstractPlayerInteraction.java | 26 +- src/scripting/event/EventInstanceManager.java | 437 +- src/scripting/event/EventManager.java | 12 +- .../event/worker/EventScriptScheduler.java | 97 +- src/scripting/item/ItemScriptManager.java | 2 +- src/scripting/map/MapScriptManager.java | 2 +- src/server/MapleItemInformationProvider.java | 10 +- src/server/ThreadManager.java | 72 + src/server/life/MapleMonster.java | 87 +- src/server/life/MapleNPC.java | 8 +- src/server/life/MobSkill.java | 4 +- .../maps/AbstractAnimatedMapleMapObject.java | 17 + src/server/maps/MapleDoorObject.java | 20 +- src/server/maps/MapleDragon.java | 8 +- src/server/maps/MapleHiredMerchant.java | 9 +- src/server/maps/MapleKite.java | 1 - src/server/maps/MapleMap.java | 57 +- src/server/maps/MapleSummon.java | 5 +- src/tools/MaplePacketCreator.java | 64 +- wz/Item.wz/Etc/0400.img.xml | 1 + wz/Map.wz/Map/Map2/200010000.img.xml | 14 - wz/Map.wz/Map/Map2/240010200.img.xml | 14 - wz/Map.wz/Map/Map2/261000000.img.xml | 2770 +++--- wz/Map.wz/Map/Map6/600000000.img.xml | 36 +- wz/Map.wz/Map/Map8/801000000.img.xml | 8171 ++++++++--------- wz/Quest.wz/Act.img.xml | 2 - wz/Quest.wz/QuestInfo.img.xml | 2 +- wz/Skill.wz/1310.img.xml | 20 + wz/Skill.wz/1311.img.xml | 40 +- wz/String.wz/Consume.img.xml | 2 +- 120 files changed, 7424 insertions(+), 6387 deletions(-) rename tools/SQL/userstances.txt => docs/moveactions.txt (100%) create mode 100644 docs/npcmarkups.txt create mode 100644 scripts/event/4jship.js create mode 100644 scripts/event/4jsuper.js create mode 100644 scripts/portal/s4ship_out.js create mode 100644 scripts/portal/s4super_out.js create mode 100644 src/net/server/channel/handlers/GrenadeEffectHandler.java create mode 100644 src/server/ThreadManager.java diff --git a/README.md b/README.md index 3c9b4feb60..31c1d32180 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Regarding distributability and usage of the code presented here: like it was bef This is a NetBeans 8.0.2 Project, that MUST be built and run under JDK/JRE 7 (1.7.0_79+) in order to run properly. This means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application. -In this project, many gameplay-wise issues generated from either the original WZ files and the server source have been partially or completely solved. Considering the use of the provided edited WZ's and server-side wz.xml files should be of the greatest importance when dealing with this instance of server source, in order to perceive it at it's full potential. My opinion, though! Refer to "README_wzchanges.txt" for more information on what has been changed from Nexon's v83 WZ files. +In this project, many gameplay-wise issues generated from either the original WZ files and the server source have been partially or completely solved. Considering the use of the provided edited WZ's and server-side wz.xml files should be of the greatest importance when dealing with this instance of server source, in order to perceive it at it's full potential. My opinion, though! The main objective of this project is to try as best as possible to recreate what once was the original MapleStory v83, while adding up some flavors that spices up the gameplay. In other words, aim to get the best of the MapleStory of that era. @@ -48,7 +48,7 @@ Client files & general tools: https://drive.google.com/drive/folders/0BzDsHSr-0V * Fraysa's https://hostr.co/gJbLZITRVHmv - * MapleSilver's starting on window-mode. + * Eric's MapleSilver starting on window-mode. --- ### Development information @@ -175,7 +175,7 @@ Finally, select "Clean and Build project" to build the JAR file for the MapleSto The client's set-up is quite straightforward: 1. From "ManagerMsv83.exe", install MapleStory on your folder of preference (e.g. "C:\Nexon\MapleStory") and follow their instructions. -2. Once done, erase these files: "HShield" (folder), "ASPLauncher.exe", "MapleStory.exe" and "patcher.exe". +2. Once done, erase these files: "HShield" (folder), "ASPLnchr.exe", "MapleStory.exe" and "Patcher.exe". 3. Extract into the client folder the "localhost.exe" from the provided link. 4. Overwrite the original WZ files with the ones provided from either one of those folders on the Google Drive: - "commit???_wz" (last published RELEASE, referring to commit of same number). diff --git a/docs/feature_list.md b/docs/feature_list.md index 985b3e0729..35b5017b61 100644 --- a/docs/feature_list.md +++ b/docs/feature_list.md @@ -89,6 +89,7 @@ Cash & Items: * Storage with "Arrange Items" feature functional. * Close-quarters evaluation mode for items (sandbox). * Further improved Karma scissors & Untradeable items mechanics. +* Reviewed pet/item position data inconsistency within CASH inventory. * Spikes on shoes. * Vega's spell. * Owl of Minerva. @@ -175,6 +176,7 @@ Server potentials: * Delete Character (requires ENABLE_PIC activated). * Smoothed up view-all-char feature, now showing properly all available characters and not disconnecting players too often. * Centralized getcurrenttime throughout several server handlers, boosting it's performance overall. +* Centralized server timestamping, several timestamps received from clients are now unused, preventing some spammable exploits. * Autosaver (periodically saves on DB current state of every player in-game). * Both fixed and randomized versions of HP/MP growth rate available, regarding player job (enable one at ServerConstants). Placeholder for HP/MP washing feature. * Implemented methods to get the current Players' MaxHP/MaxMP method with equipment HP/MP gains already summed up. @@ -191,6 +193,7 @@ Custom NPCs: * Asia: scroll & rarities shop NPC. * Abdula: lists droppers of needed skill/mastery books. * Agent E: accessory crafter. +* Dalair: automatized equipment-merger. * Donation Box: automatized item-buyer. * Coco & Ace of Hearts: C. scroll crafters. @@ -239,6 +242,7 @@ Project: * Reviewed SQL data, eliminating duplicated entries on the tables. * Improved login phase, using cache over DB queries. * Usage of HikariCP to improve the DB connection management. +* Usage of Java Threadpool to improve runnable call management. * Developed many survey tools for content profiling. * Developed a robust anti-exploit login coordinator system. * Protected many flaws with login management system. diff --git a/tools/SQL/userstances.txt b/docs/moveactions.txt similarity index 100% rename from tools/SQL/userstances.txt rename to docs/moveactions.txt diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 22fc3a580c..56831b2afe 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -2,12 +2,15 @@ Spiegelmann -> 2042000 Coco -> 9000017 Agent E -> 9000036 + Dalair -> 9000040 Donation Box -> 9000041 - Abdula -> 9209000 + Abdula -> 9209000 * -CUSTOM NPC SHOPS: - Asia -> 2082014 - T-1337 -> 9201101 +CUSTOM NPC SHOPS (db_shopupdate.sql): + Asia -> 2082014 * + T-1337 -> 9201101 * + + * : those won't get disabled when USE_ENABLE_CUSTOM_NPC_SCRIPTS = false LOGS: @@ -1414,4 +1417,40 @@ Removido possibilidade de colocar itens "untradeable" no Duey. Removido possibilidade de preparar engagements enquanto segurando aneis de casamento. 23 Outubro 2018, -Adicionado script para quest de 4o job de Cygnus Knights. \ No newline at end of file +Adicionado script para quest de 4o job de Cygnus Knights. + +25 - 27 Outubro 2018, +Scripts de eventos agora executam em uma thread separada, assim resolvendo possíveis problemas de deadlocks. +Realizada tentativa de solução para casos onde jogadores percebem mobs em pontos diferentes do mapa em certas situações. +Clareado um problema de deadlock relacionado a packets de visão/desaparecimento de jogadores e mobs. +Resolvido diversos problemas de deadlocks relacionados com event instances. +Implementado ThreadManager. Este sistema utiliza a abordagem de ThreadPool para reciclagem de threads utilizadas, assim eliminando realização de overhead ao criar novas threads a todo uso. +Resolvido certos casos onde requisição de item pickup de jogador não era devidamente registrado pelo servidor como ação válida. + +29 - 31 Outubro 2018, +Corrigido mobs não lançando skills em determinados casos. Comportamento notável era mobs de nível baixo utilizando nenhuma skill. +Skill Storm Break não mais remove flechas ao ser utilizada. +Implementado método que força alteração na posição atual de mobs no mapa. +Mobs que forem atacados de muito longe terão suas posições atualizadas para todos os jogadores no mapa, buscando assim evitar dessincronizações entre clientes. +Adicionado drops de red bean porridge em alguns mobs de El Nath. +Implementado flag para opção de aquisição de belts de Dojo regular ou fácil. +Corrigido map banish de mobs não funcionando corretamente após as últimas atualizações do MoveLifeHandler. +Corrigido bug na quest do 2nd Maker que permitia conclusão de quest ao realizar disassemble em equipamentos. +Adicionado nova mecânica custom: amalgama de equipamentos. Jogadores acima do nível 160, que possuem Maker level 3, poderão utilizar esta mecânica para alavancar atributos dos itens equipados. +Refatorado método de ganho de fama para não mais usar modificador synchronized, assim evitando possível caso de deadlock. +Adicionado server flag que permite acesso a todas as skills de job de Aran ao iniciar novo job. +Corrigido horário irregular no sistema do servidor. +Adicionado scripts para skill quests de Battleship e Super Transformation. + +03 Novembro 2018, +Corrigido caso onde pets estavam com posições de inventário desatualizadas, levando a certas inconsistências com evoluir/administrar pets. + +05 Novembro 2018, +Alterado administração de timestamp nos handlers. Agora detecção de spam é inteiramente responsabilidade do servidor, onde a detecção de spam ocorre de acordo com quantidade de vezes que um pacote é recebido num dado intervalo de tempo estipulado internamente. +Refatorado packets que lidam com respostas a comandos de pets. + +07 - 09 Novembro 2018, +Corrigido drop data de Ultra Gray com item inexistente. +Corrigido quest de meteoritos mudando estado da quest mesmo quando jogador não recebe o item devido a inventário cheio. +Corrigido quest de Maker lv1 retirando mesos além da quantidade esperada, ao realizar ações da quest. +Adicionado server flag que lida com utilização de features NPCs custom. \ No newline at end of file diff --git a/docs/npcmarkups.txt b/docs/npcmarkups.txt new file mode 100644 index 0000000000..40b59731b0 --- /dev/null +++ b/docs/npcmarkups.txt @@ -0,0 +1,28 @@ +Source: http://metropi.forumotion.net/t32-npc-scripting-guide-from-ragezone + +NPC Markups: +#b = Blue text. +#c[itemid]# Shows how many [itemid] the player has in their inventory. +#d = Purple text. +#e = Bold text. +#f[imagelocation]# - Shows an image inside the .wz files. +#g = Green text. +#h # - Shows the name of the player. +#i[itemid]# - Shows a picture of the item. +#k = Black text. +#l - Selection close. +#m[mapid]# - Shows the name of the map. +#n = Normal text (removes bold). +#o[mobid]# - Shows the name of the mob. +#p[npcid]# - Shows the name of the NPC. +#q[skillid]# - Shows the name of the skill. +#r = Red text. +#s[skillid]# - Shows the image of the skill. +#t[itemid]# - Shows the name of the item. +#v[itemid]# - Shows a picture of the item. +#x - Returns "0%" (need more information on this). +#z[itemid]# - Shows the name of the item. +#B[%]# - Shows a 'progress' bar. +#F[imagelocation]# - Shows an image inside the .wz files. +#L[number]# Selection open. +\r\n - Moves down a line. \ No newline at end of file diff --git a/scripts/event/4jship.js b/scripts/event/4jship.js new file mode 100644 index 0000000000..eda4afffdd --- /dev/null +++ b/scripts/event/4jship.js @@ -0,0 +1,138 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/** + * @Author Ronan + * Event - Kyrin's Test Quest +**/ + +var entryMap = 912010000; +var exitMap = 120000101; + +var minMapId = 912010000; +var maxMapId = 912010200; + +var eventTime = 4; //4 minutes + +var lobbyRange = [0, 0]; + +function setLobbyRange() { + return lobbyRange; +} + +function init() { + em.setProperty("noEntry","false"); +} + +function setup(level, lobbyid) { + var eim = em.newInstance("4jship_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + eim.setProperty("canLeave", "0"); + + eim.getInstanceMap(entryMap).resetPQ(level); + + respawnStages(eim); + eim.startEventTimer(eventTime * 60000); + eim.schedule("playerCanLeave", 1 * 60000); + eim.schedule("playerSurvived", 2 * 60000); + return eim; +} + +function afterSetup(eim) {} + +function respawnStages(eim) {} + +function playerCanLeave(eim) { + eim.setIntProperty("canLeave", 1); +} + +function playerSurvived(eim) { + if (eim.getLeader().isAlive()) { + eim.setIntProperty("canLeave", 2); + eim.dropMessage(5, "Kyrin: You have passed the test. Now for the closing part... Are you able reach the exit over there?"); + } else { + eim.dropMessage(5, "Kyrin: You have failed the test. Aww, don't have such a sad face, just try it again later, ok?"); + } +} + +function playerEntry(eim, player) { + var map = eim.getMapInstance(entryMap); + player.changeMap(map, map.getPortal(0)); +} + +function playerUnregistered(eim, player) {} + +function playerExit(eim, player) { + eim.unregisterPlayer(player); + eim.dispose(); + em.setProperty("noEntry","false"); +} + +function playerLeft(eim, player) {} + +function scheduledTimeout(eim) { + var player = eim.getPlayers().get(0); + playerExit(eim, player); + player.changeMap(exitMap); +} + +function playerDisconnected(eim, player) { + playerExit(eim, player); +} + +function changedMap(eim, chr, mapid) { + if(mapid < minMapId || mapid > maxMapId) playerExit(eim, chr); +} + +function clearPQ(eim) { + eim.stopEventTimer(); + eim.setEventCleared(); + + var player = eim.getPlayers().get(0); + eim.unregisterPlayer(player); + player.changeMap(exitMap); + + eim.dispose(); + em.setProperty("noEntry","false"); +} + +function monsterKilled(mob, eim) {} + +function leftParty(eim, player) {} + +function disbandParty(eim) {} + +function monsterValue(eim, mobId) { + return 1; +} + +function friendlyKilled(mob, eim) { + if(em.getProperty("noEntry") != "false") { + var player = eim.getPlayers().get(0); + playerExit(eim, player); + player.changeMap(exitMap); + } +} + +function allMonstersDead(eim) {} + +function cancelSchedule() {} + +function dispose() {} diff --git a/scripts/event/4jsuper.js b/scripts/event/4jsuper.js new file mode 100644 index 0000000000..565d7c6f27 --- /dev/null +++ b/scripts/event/4jsuper.js @@ -0,0 +1,140 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/** + * @Author Ronan + * Event - Kyrin's Test Quest +**/ + +var entryMap = 912010100; +var exitMap = 120000101; + +var minMapId = 912010100; +var maxMapId = 912010200; + +var eventTime = 4; //4 minutes + +var lobbyRange = [0, 0]; + +function setLobbyRange() { + return lobbyRange; +} + +function init() { + em.setProperty("noEntry","false"); +} + +function setup(level, lobbyid) { + var eim = em.newInstance("4jsuper_" + lobbyid); + eim.setProperty("level", level); + eim.setProperty("boss", "0"); + eim.setProperty("canLeave", "0"); + + eim.getInstanceMap(entryMap).resetPQ(level); + + respawnStages(eim); + eim.startEventTimer(eventTime * 60000); + eim.schedule("playerCanLeave", 1 * 60000); + eim.schedule("playerSurvived", 2 * 60000); + return eim; +} + +function afterSetup(eim) {} + +function respawnStages(eim) {} + +function playerCanLeave(eim) { + eim.setIntProperty("canLeave", 1); +} + +function playerSurvived(eim) { + if (eim.getLeader().isAlive()) { + eim.setIntProperty("canLeave", 2); + eim.dropMessage(5, "Kyrin: You have passed the test. Now for the closing part... Are you able reach the exit over there?"); + } else { + eim.dropMessage(5, "Kyrin: You have failed the test. Aww, don't have such a sad face, just try it again later, ok?"); + } +} + +function playerEntry(eim, player) { + var map = eim.getMapInstance(entryMap); + player.changeMap(map, map.getPortal(0)); +} + +function playerUnregistered(eim, player) {} + +function playerExit(eim, player) { + eim.unregisterPlayer(player); + eim.dispose(); + em.setProperty("noEntry","false"); +} + +function playerLeft(eim, player) {} + +function scheduledTimeout(eim) { + var player = eim.getPlayers().get(0); + playerExit(eim, player); + player.changeMap(exitMap); +} + +function playerDisconnected(eim, player) { + playerExit(eim, player); +} + +function changedMap(eim, chr, mapid) { + if(mapid < minMapId || mapid > maxMapId) playerExit(eim, chr); +} + +function changedLeader(eim, leader) {} + +function clearPQ(eim) { + eim.stopEventTimer(); + eim.setEventCleared(); + + var player = eim.getPlayers().get(0); + eim.unregisterPlayer(player); + player.changeMap(exitMap); + + eim.dispose(); + em.setProperty("noEntry","false"); +} + +function monsterKilled(mob, eim) {} + +function leftParty(eim, player) {} + +function disbandParty(eim) {} + +function monsterValue(eim, mobId) { + return 1; +} + +function friendlyKilled(mob, eim) { + if(em.getProperty("noEntry") != "false") { + var player = eim.getPlayers().get(0); + playerExit(eim, player); + player.changeMap(exitMap); + } +} + +function allMonstersDead(eim) {} + +function cancelSchedule() {} + +function dispose() {} diff --git a/scripts/npc/1032000.js b/scripts/npc/1032000.js index 985f1b7191..3a1c038675 100644 --- a/scripts/npc/1032000.js +++ b/scripts/npc/1032000.js @@ -3,6 +3,7 @@ var maps = [104000000, 102000000, 100000000, 103000000, 120000000]; var cost = [1000, 1000, 1000, 1000, 800]; var selectedMap = -1; var mesos; +var hasCoupon = false; function start() { cm.sendNext("Hello, I drive the Regular Cab. If you want to go from town to town safely and fast, then ride our cab. We'll glady take you to your destination with an affordable price."); @@ -33,22 +34,33 @@ function action(mode, type, selection) { selStr += "\r\n#L" + i + "##m" + maps[i] + "# (" + (cm.getJobId() == 0 ? cost[i] / 10 : cost[i]) + " mesos)#l"; cm.sendSimple(selStr); } else if (status == 2) { - cm.sendYesNo("You don't have anything else to do here, huh? Do you really want to go to #b#m" + maps[selection] + "##k? It'll cost you #b"+ (cm.getJobId() == 0 ? cost[selection] / 10 : cost[selection]) + " mesos#k."); + if (maps[selection] == 100000000 && cm.getMapId() == 101000000 && cm.haveItem(4032288)) { + cm.sendYesNo("Hmm, I see you have been recommended by Neinheart to come to Victoria Island to improve your knightly skills. Well, just this time the ride will be free of charges. Will you take the ride?"); + hasCoupon = true; + } else { + cm.sendYesNo("You don't have anything else to do here, huh? Do you really want to go to #b#m" + maps[selection] + "##k? It'll cost you #b"+ (cm.getJobId() == 0 ? cost[selection] / 10 : cost[selection]) + " mesos#k."); + } + selectedMap = selection; } else if (status == 3) { - if (cm.getJobId() == 0) { - mesos = cost[selectedMap] / 10; + if (!hasCoupon) { + if (cm.getJobId() == 0) { + mesos = cost[selectedMap] / 10; + } else { + mesos = cost[selectedMap]; + } + + if (cm.getMeso() < mesos) { + cm.sendNext("You don't have enough mesos. Sorry to say this, but without them, you won't be able to ride the cab."); + cm.dispose(); + return; + } + + cm.gainMeso(-mesos); } else { - mesos = cost[selectedMap]; + cm.gainItem(4032288, -1); } - if (cm.getMeso() < mesos) { - cm.sendNext("You don't have enough mesos. Sorry to say this, but without them, you won't be able to ride the cab."); - cm.dispose(); - return; - } - - cm.gainMeso(-mesos); cm.warp(maps[selectedMap], 0); cm.dispose(); } diff --git a/scripts/npc/1052014.js b/scripts/npc/1052014.js index 315221803f..ec0aee2287 100644 --- a/scripts/npc/1052014.js +++ b/scripts/npc/1052014.js @@ -79,7 +79,7 @@ function action(mode, type, selection) { if(status == 0) { hasCoin = cm.haveItem(coinId); - cm.sendNext("This is the vending machine of the Internet Cafe. Place your erasers or #t" + coinId + "# earned throughout the quests to redeem a prize. You can place #bany amount of erasers#k, however take note that bigger shots improves the reward possibilities!"); + cm.sendNext("This is the vending machine of the Internet Cafe. Place your erasers or #t" + coinId + "# earned throughout the quests to redeem a prize. You can place #bany amount of erasers#k, however take note that placing #rdifferent erasers#k and #rbigger shots of any of them#k will improve the reward possibilities!"); } else if(status == 1) { var sendStr; currentTier = getRewardTier(); diff --git a/scripts/npc/1090000.js b/scripts/npc/1090000.js index 575c7aaffb..dfc900e45a 100644 --- a/scripts/npc/1090000.js +++ b/scripts/npc/1090000.js @@ -33,8 +33,39 @@ spawnPnpc = false; spawnPnpcFee = 7000000; jobType = 5; +var advQuest = 0; function start() { - if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { + if (cm.isQuestStarted(6330)) { + if (cm.getEventInstance() != null) { // missing script for skill test found thanks to Lost(tm) + advQuest = 5; // string visibility thanks to iPunchEm & Glvelturall + cm.sendNext("Not bad at all. Let's discuss this outside!"); + cm.setQuestProgress(6330, 0, 1); + } else if (cm.getQuestProgress(6330, 0) == 0) { + advQuest = 1; + cm.sendNext("You're ready, right? Now try to withstand my attacks for 2 minutes. I won't go easy on you. Good luck, because you will need it."); + } else { + advQuest = 3; + cm.teachSkill(5121003, 0, 10, -1); + cm.forceCompleteQuest(6330); + + cm.sendNext("Congratulations. You have managed to pass my test. I'll teach you a new skill called \"Super Transformation\".\r\n\r\n #s5221003# #b#q5221003##k"); + } + } else if (cm.isQuestStarted(6370)) { + if (cm.getEventInstance() != null) { + advQuest = 6; + cm.sendNext("Not bad at all. Let's discuss this outside!"); + cm.setQuestProgress(6370, 0, 1); + } else if (cm.getQuestProgress(6370, 0) == 0) { + advQuest = 2; + cm.sendNext("You're ready, right? Now try to withstand my attacks for 2 minutes. I won't go easy on you. Good luck, because you will need it."); + } else { + advQuest = 4; + cm.teachSkill(5221006, 0, 10, -1); + cm.forceCompleteQuest(6370); + + cm.sendNext("Congratulations. You have managed to pass my test. I'll teach you a new skill called \"Battleship\".\r\n\r\n #s5221006# #b#q5221006##k"); + } + } else if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) { spawnPnpc = true; var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, haven't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?"; @@ -82,7 +113,24 @@ function action(mode, type, selection) { start(); return; } else { - if(spawnPnpc) { + if (advQuest > 0) { + if (advQuest < 3) { + var em = cm.getEventManager(advQuest == 1 ? "4jship" : "4jsuper"); + if(!em.startInstance(cm.getPlayer())) { + cm.sendOk("Someone is already challenging the test. Please try again later."); + } + } else if (advQuest < 5) { + if (advQuest == 3) { + cm.sendOk("It is similar to that of 'Transformation', but it's much more powerful than that. Keep training, and hope to see you around."); + } else { + cm.sendOk("Unlike most of the other skills you used as a Pirate, this one definitely is different. You can actually ride the 'Battleship' and attack enemies with it. Your DEF level will increase for the time you're on board, so that'll help you tremendously in combat situations. May you become the best Gunslinger out there..."); + } + } else { + cm.warp(120000101); + } + + cm.dispose(); + } else if(spawnPnpc) { if(mode > 0) { if(cm.getMeso() < spawnPnpcFee) { cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame."); diff --git a/scripts/npc/1100003.js b/scripts/npc/1100003.js index 11b909b3f2..888d506e5b 100644 --- a/scripts/npc/1100003.js +++ b/scripts/npc/1100003.js @@ -12,12 +12,8 @@ var menu = new Array("Victoria Island"); var method; -var hasCoupon = false; - function start() { status = -1; - if(cm.haveItem(4032288)) hasCoupon = true; - action(1, 0, 0); } @@ -36,22 +32,13 @@ function action(mode, type, selection) { } status++; if (status == 0) { - if(!hasCoupon) { - var display = ""; - for(var i=0; i < menu.length; i++) { - display += "\r\n#L"+i+"##b Victoria Island (1000 mesos)#k"; - } - cm.sendSimple("Eh, Hello...again. Do you want to leave Ereve and go somewhere else? If so, you've come to the right place. I operate a ferry that goes from #bEreve#k to #bVictoria Island#k, I can take you to #bVictoria Island#k if you want... You'll have to pay a fee of #b1000#k Mesos.\r\n"+display); - } else { - cm.sendYesNo("Hmm, hi there. I see you have been recommended by Neinheart to go to Victoria Island to improve your knightly skills. Well, just this time the ride will be free of charges. Will you embark?"); - } - + var display = ""; + for(var i=0; i < menu.length; i++) { + display += "\r\n#L"+i+"##b Victoria Island (1000 mesos)#k"; + } + cm.sendSimple("Eh, Hello...again. Do you want to leave Ereve and go somewhere else? If so, you've come to the right place. I operate a ferry that goes from #bEreve#k to #bVictoria Island#k, I can take you to #bVictoria Island#k if you want... You'll have to pay a fee of #b1000#k Mesos.\r\n"+display); } else if(status == 1) { - if(hasCoupon) { - cm.gainItem(4032288, -1); - cm.warp(200090031); - cm.dispose(); - } else if(cm.getMeso() < 1000) { + if(cm.getMeso() < 1000) { cm.sendNext("Hmm... Are you sure you have #b1000#k Mesos? Check your Inventory and make sure you have enough. You must pay the fee or I can't let you get on..."); cm.dispose(); } else { diff --git a/scripts/npc/1100006.js b/scripts/npc/1100006.js index f7b3c68ebb..02b897740e 100644 --- a/scripts/npc/1100006.js +++ b/scripts/npc/1100006.js @@ -10,6 +10,6 @@ **/ function start() { - cm.sendOk("Ah, such lovely winds. This should be a perfect voyage as long as no stupid customer falls off for attempting some weird skill. Of course, I'm talking about you. Please refain from using your skills."); + cm.sendOk("Ah, such lovely winds. This should be a perfect voyage as long as no stupid customer falls off for attempting some weird skill. Of course, I'm talking about you. Please refrain from using your skills."); cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/1101001.js b/scripts/npc/1101001.js index 1bad42e68a..7837038476 100644 --- a/scripts/npc/1101001.js +++ b/scripts/npc/1101001.js @@ -1,11 +1,15 @@ /* NPC Name: Divine Bird Map(s): Erev - Description: Buff + Description: 3rd job KoC Buff */ +importPackage(Packages.constants); function start() { - cm.useItem(2022458); + if (cm.getPlayer().isCygnus() && GameConstants.getJobBranch(cm.getJob()) > 2) { + cm.useItem(2022458); + } + cm.sendOk("Don't stop training. Every ounce of your energy is required to protect the world of Maple...."); - cm.dispose(); + cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/2042000.js b/scripts/npc/2042000.js index 5387617294..45d5ef912b 100644 --- a/scripts/npc/2042000.js +++ b/scripts/npc/2042000.js @@ -49,18 +49,24 @@ function action(mode, type, selection) { status--; if(status == 0) { + if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + cm.sendOk("The Monster Carnival is currently unavailable."); + cm.dispose(); + return; + } + var selStr = "The Monster Carnival is currently unavailable, but instead I offer a steadfast #bore refining#k service for you, taxing #r" + ((feeMultiplier * 100) | 0) + "%#k over the usual fee to synthetize them. What will you do?#b"; var options = new Array("Refine mineral ores","Refine jewel ores"); if(refineCrystals) { - options.push("Refine crystal ores"); + options.push("Refine crystal ores"); } if(refineRocks) { - options.push("Refine plates/jewels"); + options.push("Refine plates/jewels"); } for (var i = 0; i < options.length; i++){ - selStr += "\r\n#L" + i + "# " + options[i] + "#l"; + selStr += "\r\n#L" + i + "# " + options[i] + "#l"; } cm.sendSimple(selStr); diff --git a/scripts/npc/2050014.js b/scripts/npc/2050014.js index 4803367126..5a43f0112e 100644 --- a/scripts/npc/2050014.js +++ b/scripts/npc/2050014.js @@ -47,10 +47,14 @@ function action(mode, type, selection) { var progress = cm.getQuestProgress(3421, 0); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { - progress |= (1 << meteoriteId); - - cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + if (cm.canHold(4031117, 1)) { + progress |= (1 << meteoriteId); + + cm.gainItem(4031117, 1); + cm.setQuestProgress(3421, 0, progress); + } else { + cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); + } } } diff --git a/scripts/npc/2050015.js b/scripts/npc/2050015.js index 4803367126..5a43f0112e 100644 --- a/scripts/npc/2050015.js +++ b/scripts/npc/2050015.js @@ -47,10 +47,14 @@ function action(mode, type, selection) { var progress = cm.getQuestProgress(3421, 0); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { - progress |= (1 << meteoriteId); - - cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + if (cm.canHold(4031117, 1)) { + progress |= (1 << meteoriteId); + + cm.gainItem(4031117, 1); + cm.setQuestProgress(3421, 0, progress); + } else { + cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); + } } } diff --git a/scripts/npc/2050016.js b/scripts/npc/2050016.js index 4803367126..5a43f0112e 100644 --- a/scripts/npc/2050016.js +++ b/scripts/npc/2050016.js @@ -47,10 +47,14 @@ function action(mode, type, selection) { var progress = cm.getQuestProgress(3421, 0); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { - progress |= (1 << meteoriteId); - - cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + if (cm.canHold(4031117, 1)) { + progress |= (1 << meteoriteId); + + cm.gainItem(4031117, 1); + cm.setQuestProgress(3421, 0, progress); + } else { + cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); + } } } diff --git a/scripts/npc/2050017.js b/scripts/npc/2050017.js index 4803367126..5a43f0112e 100644 --- a/scripts/npc/2050017.js +++ b/scripts/npc/2050017.js @@ -47,10 +47,14 @@ function action(mode, type, selection) { var progress = cm.getQuestProgress(3421, 0); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { - progress |= (1 << meteoriteId); - - cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + if (cm.canHold(4031117, 1)) { + progress |= (1 << meteoriteId); + + cm.gainItem(4031117, 1); + cm.setQuestProgress(3421, 0, progress); + } else { + cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); + } } } diff --git a/scripts/npc/2050018.js b/scripts/npc/2050018.js index 4803367126..5a43f0112e 100644 --- a/scripts/npc/2050018.js +++ b/scripts/npc/2050018.js @@ -47,10 +47,14 @@ function action(mode, type, selection) { var progress = cm.getQuestProgress(3421, 0); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { - progress |= (1 << meteoriteId); - - cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + if (cm.canHold(4031117, 1)) { + progress |= (1 << meteoriteId); + + cm.gainItem(4031117, 1); + cm.setQuestProgress(3421, 0, progress); + } else { + cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); + } } } diff --git a/scripts/npc/2050019.js b/scripts/npc/2050019.js index 4803367126..5a43f0112e 100644 --- a/scripts/npc/2050019.js +++ b/scripts/npc/2050019.js @@ -47,10 +47,14 @@ function action(mode, type, selection) { var progress = cm.getQuestProgress(3421, 0); if((progress >> meteoriteId) % 2 == 0 || (progress == 63 && !cm.haveItem(4031117, 6))) { - progress |= (1 << meteoriteId); - - cm.gainItem(4031117, 1); - cm.setQuestProgress(3421, 0, progress); + if (cm.canHold(4031117, 1)) { + progress |= (1 << meteoriteId); + + cm.gainItem(4031117, 1); + cm.setQuestProgress(3421, 0, progress); + } else { + cm.getPlayer().dropMessage(1, "Have a ETC slot available for this item."); + } } } diff --git a/scripts/npc/2091005.js b/scripts/npc/2091005.js index 18a06008c4..6c9f1abed2 100644 --- a/scripts/npc/2091005.js +++ b/scripts/npc/2091005.js @@ -25,26 +25,27 @@ * @Map(s): Dojo Hall */ importPackage(Packages.server.maps); +importPackage(Packages.constants); var disabled = false; var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004); var belt_level = Array(25, 35, 45, 60, 75); var belt_on_inventory; - -/* var belt_points = Array(200, 1800, 4000, 9200, 17000); */ -var belt_points = Array(10, 90, 200, 460, 850); /* Watered down version */ +var belt_points; var status = -1; var selectedMenu = -1; var dojoWarp = 0; function start() { - if(disabled) { + if (disabled) { cm.sendOk("My master has requested that the dojo be #rclosed#k at this time so I can't let you in."); cm.dispose(); return; } + belt_points = ServerConstants.USE_FAST_DOJO_UPGRADE ? Array(10, 90, 200, 460, 850) : Array(200, 1800, 4000, 9200, 17000); + belt_on_inventory = new Array(); for (var i = 0; i < belts.length; i++) { belt_on_inventory.push(cm.haveItemWithId(belts[i], true)); diff --git a/scripts/npc/2101013.js b/scripts/npc/2101013.js index d1e2e42606..f2cc998982 100644 --- a/scripts/npc/2101013.js +++ b/scripts/npc/2101013.js @@ -14,7 +14,7 @@ function action(mode, type, selection) { if (mode == -1) { cm.dispose(); } else { - if (status == 0 && mode == 0) { + if (mode == 0) { cm.sendNext("Aye...are you scared of speed or heights? You can't trust my flying skills? Trust me, I've worked out all the kinks!"); cm.dispose(); return; @@ -26,7 +26,7 @@ function action(mode, type, selection) { if(status == 0){ cm.sendNext("I don't know how you found out about this, but you came to the right place! For those that wandered around Nihal Desert and are getting homesick, I am offering a flight straight to Victorial Island, non-stop! Don't worry about the flying ship--it's only fallen once or twice! Don't you feel claustrophobic being in a long flight on that small ship?"); } else if(status == 1){ - cm.sendAcceptDecline("Please remember two things. One, this line is actually for overseas shipping, so #rI cannot gurantee exactly which town you'll land#k. Two, since I am putting you in this special flight, it'll be a bit expensive. The service charge is #e#b10,000 mesos#n#k. There's a flight thats about to take off. Are you interested in this direct flight?"); + cm.sendYesNo("Please remember two things. One, this line is actually for overseas shipping, so #rI cannot gurantee exactly which town you'll land#k. Two, since I am putting you in this special flight, it'll be a bit expensive. The service charge is #e#b10,000 mesos#n#k. There's a flight thats about to take off. Are you interested in this direct flight?"); } else if(status == 2){ cm.sendNext("Okay, ready to takeoff~"); } else if(status == 3){ diff --git a/scripts/npc/9000017.js b/scripts/npc/9000017.js index 08e5211ad9..57e58dde3c 100644 --- a/scripts/npc/9000017.js +++ b/scripts/npc/9000017.js @@ -51,6 +51,12 @@ function action(mode, type, selection) { } if (status == 0) { + if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k."); + cm.dispose(); + return; + } + var selStr = "Hey traveler! Come, come closer... We offer a #bhuge opportunity of business#k to you. If you want to know what it is, keep listening..."; cm.sendNext(selStr); } diff --git a/scripts/npc/9000036.js b/scripts/npc/9000036.js index abeb0e696a..ba2581eedd 100644 --- a/scripts/npc/9000036.js +++ b/scripts/npc/9000036.js @@ -39,6 +39,12 @@ var equip; var maxEqp = 0; function start() { + if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + cm.sendOk("Hi, I'm #b#p" + cm.getNpc() + "##k."); + cm.dispose(); + return; + } + cm.getPlayer().setCS(true); var selStr = "Hello, I am the #bAccessory NPC Crafter#k! My works are widely recognized to be too fine, up to the point at which all my items mimic not only the appearance but too the attributes of them! Everything I charge is some 'ingredients' to make them and, of course, a fee for my services. On what kind of equipment are you interessed?#b"; var options = ["Pendants","Face accessories","Eye accessories","Belts & medals","Rings"/*,"#t4032496#"*/]; @@ -56,7 +62,7 @@ function action(mode, type, selection) { if (status == 0) { if (selection == 0) { //pendants var selStr = "Well, I've got these pendants on my repertoire:#b"; - items = [1122018,1122007,1122001,1122002,1122003,1122004,1122005,1122006,1122058]; + items = [1122018,1122007,1122001,1122003,1122004,1122006,1122002,1122005,1122058]; for (var i = 0; i < items.length; i++) selStr += "\r\n#L" + i + "##t" + items[i] + "##b"; }else if (selection == 1) { //face accessory @@ -103,9 +109,9 @@ function action(mode, type, selection) { if (selectedType != 3) selectedItem = selection; if (selectedType == 0) { //pendant refine - var matSet = [[4003004, 4030012, 4001356, 4000026], [4000026, 4001356, 4000073, 4001006], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [4001344, 4003001, 4003004, 4003005], [1122007, 4003002, 4000413]]; - var matQtySet = [[20, 20, 5, 1], [5, 5, 10, 1], [10, 4, 20, 4], [20, 8, 20, 8], [10, 4, 20, 4], [15, 6, 30, 6], [20, 8, 40, 8], [15, 6, 30, 6], [1, 1, 1]]; - var costSet = [150000, 500000, 200000, 400000, 200000, 300000, 400000, 300000, 2500000]; + var matSet = [[4003004, 4030012, 4001356, 4000026], [4000026, 4001356, 4000073, 4001006], [4001343, 4011002, 4003004, 4003005], [4001343, 4011006, 4003004, 4003005], [4000091, 4011005, 4003004, 4003005], [4000091, 4011001, 4003004, 4003005], [4000469, 4011000, 4003004, 4003005], [4000469, 4011004, 4003004, 4003005], [1122007, 4003002, 4000413]]; + var matQtySet = [[20, 20, 5, 1], [5, 5, 10, 1], [10, 2, 20, 4], [10, 1, 20, 4], [15, 3, 30, 6], [15, 3, 30, 6], [20, 5, 20, 8], [20, 4, 40, 8], [1, 1, 1]]; + var costSet = [150000, 500000, 200000, 200000, 300000, 300000, 400000, 400000, 2500000]; }else if (selectedType == 1) { //face accessory refine var matSet = [[4006000, 4003004],[4006000, 4003004,4000026],[4006000, 4003004,4000026,4000082,4003002],[4006000, 4003005],[4006000, 4003005,4000026],[4006000, 4003005,4000026,4000082,4003002],[4001006, 4011008],[4001006, 4011008],[4001006, 4011008],[4001006, 4011008]]; var matQtySet = [[5,5],[5,5,5],[5,5,5,5,1],[5,5],[5,5,5],[5,5,5,5,1],[1,1],[1,1],[1,1],[1,1]]; @@ -145,9 +151,9 @@ function action(mode, type, selection) { var prompt = "You want me to make "; if(selectedType != 3) { if (qty == 1) - prompt += "a #t" + item + "#?"; + prompt += "a #b#t" + item + "##k?"; else - prompt += qty + " #t" + item + "#?"; + prompt += "#b" + qty + " #t" + item + "##k?"; } else prompt += "a #bbelt#k or a #bmedal#k?"; diff --git a/scripts/npc/9000040.js b/scripts/npc/9000040.js index a748d23afa..004ba79aab 100644 --- a/scripts/npc/9000040.js +++ b/scripts/npc/9000040.js @@ -19,31 +19,72 @@ */ /* Dalair Medal NPC. + + NPC Equipment Merger: + * @author Ronan Lana */ +importPackage(Packages.client.processor); +importPackage(Packages.constants); + var status; - +var mergeFee = 50000; +var name; + function start() { - status = -1; - action(1, 0, 0); + status = -1; + action(1, 0, 0); } function action(mode, type, selection) { - if (mode == -1) { - cm.dispose(); - } else { - if (mode == 0 && type > 0) { - cm.dispose(); - return; - } - if (mode == 1) - status++; - else - status--; - - if(status == 0) { - cm.sendOk("The medal ranking system is currently unavailable."); - cm.dispose(); - } + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + cm.sendOk("The medal ranking system is currently unavailable..."); + cm.dispose(); + return; + } + + var levelLimit = !cm.getPlayer().isCygnus() ? 160 : 110; + var selStr = "The medal ranking system is currently unavailable... Therefore, I am providing the #bEquipment Merge#k service! "; + + if (!ServerConstants.USE_STARTER_MERGE && (cm.getPlayer().getLevel() < levelLimit || MakerProcessor.getMakerSkillLevel(cm.getPlayer()) < 3)) { + selStr += "However, you must have #rMaker level 3#k and at least #rlevel 110#k (Cygnus Knight), #rlevel 160#k (other classes) and a fund of #r" + cm.numberWithCommas(mergeFee) + " mesos#k to use the service."; + cm.sendOk(selStr); + cm.dispose(); + } else if (cm.getMeso() < mergeFee) { + selStr += "I'm sorry, but this service tax is of #r" + cm.numberWithCommas(mergeFee) + " mesos#k, which it seems you unfortunately don't have right now... Please, stop by again later."; + cm.sendOk(selStr); + cm.dispose(); + } else { + selStr += "For the fee of #r" + cm.numberWithCommas(mergeFee) + "#k mesos, merge unnecessary equipments in your inventory into your currently equipped gears to get stat boosts into them, statups based on the attributes of the items used on the merge!"; + cm.sendNext(selStr); + } + } else if(status == 1) { + selStr = "#rWARNING#b: Make sure you have your items ready to merge at the slots #rAFTER#b the item you have selected to merge.#k Any items #bunder#k the item selected will be merged thoroughly.\r\n\r\nNote that equipments receiving bonuses from merge are going to become #rUntradeable#k thereon, and equipments that already received the merge bonus #rcannot be used for merge#k.\r\n\r\n"; + cm.sendGetText(selStr); + } else if(status == 2) { + name = cm.getText(); + + if (cm.getPlayer().mergeAllItemsFromName(name)) { + cm.gainMeso(-mergeFee); + cm.sendOk("Merging complete! Thanks for using the service and enjoy your new equipment stats."); + } else { + cm.sendOk("There is no #b'" + name + "'#k in your #bEQUIP#k inventory!"); + } + + cm.dispose(); + } + } } diff --git a/scripts/npc/9000041.js b/scripts/npc/9000041.js index 24429a1cd5..1d2cc250ef 100644 --- a/scripts/npc/9000041.js +++ b/scripts/npc/9000041.js @@ -42,6 +42,12 @@ function action(mode, type, selection) { } if (status == 0) { + if (!Packages.constants.ServerConstants.USE_ENABLE_CUSTOM_NPC_SCRIPT) { + cm.sendOk("The medal ranking system is currently unavailable..."); + cm.dispose(); + return; + } + var selStr = "Hello, I am the #bBazaar NPC#k! Sell to me any item on your inventory you don't need. #rWARNING#b: Make sure you have your items ready to sell at the slots #rAFTER#b the item you have selected to sell.#k Any items #bunder#k the item selected will be sold thoroughly."; for (var i = 0; i < options.length; i++) selStr += "\r\n#L" + i + "# " + options[i] + "#l"; diff --git a/scripts/npc/9201056.js b/scripts/npc/9201056.js index 0d8efd7ae4..2a8686d764 100644 --- a/scripts/npc/9201056.js +++ b/scripts/npc/9201056.js @@ -20,7 +20,6 @@ along with this program. If not, see . */ var status = 0; -var goToMansion = false; var fee = 15000; function start() { @@ -37,19 +36,11 @@ function action(mode, type, selection) { else { status++; if (cm.getPlayer().getMapId() == 682000000) { - if (status == 0) - cm.sendSimple("Where to, boss? \r\n#b#L0#New Leaf City (" + fee + " mesos)#l\r\n#L1#Haunted Mansion#l#k"); - else if (status == 1) { + if (status == 0) { if (selection == 0) - cm.sendYesNo("You want to go to New Leaf City?"); - else { - goToMansion = true; - cm.sendYesNo("You're sure you want to enter the Mansion?"); - } - } else if (status == 2) { - if(goToMansion) { - cm.warp(682000100, 0); - } else if(cm.getMeso() >= fee) { + cm.sendYesNo("Would you like to return back to #bcivilization#k? The fee is " + fee + " mesos."); + } else if (status == 1) { + if(cm.getMeso() >= fee) { cm.gainMeso(-fee); cm.warp(600000000); } else { diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index c7e41c8478..691effb7e7 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -98,6 +98,7 @@ function writeFeatureTab_CashItems() { addFeature("Storage with 'Arrange Items' feature functional."); addFeature("Close-quarters evaluation mode for items."); addFeature("Reviewed Karma scissors & Untradeable items."); + addFeature("Reviewed an pet position issue within CASH inventory."); addFeature("Scroll for Spikes on Shoes."); addFeature("Scroll for Cold Protection."); addFeature("Vega's spell."); @@ -187,6 +188,7 @@ function writeFeatureTab_Serverpotentials() { addFeature("Delete Character."); addFeature("Smooth view-all-char, now showing all account chars."); addFeature("Centralized servertime, boosting handler performance."); + addFeature("Centralized timestamping, unused rcvd timestamps."); addFeature("Autosaver (periodically saves player's data on DB)."); addFeature("Fixed and randomized HP/MP growth rate available."); addFeature("Players' MaxHP/MaxMP method accounting equip gain."); @@ -211,6 +213,7 @@ function writeFeatureTab_CustomNPCs() { addFeature("Asia: scroll & rarities shop NPC."); addFeature("Abdula: lists droppers of needed skill/mastery books."); addFeature("Agent E: accessory crafter."); + addFeature("Dalair: automatized equipment-merger."); addFeature("Donation Box: automatized item-buyer."); addFeature("Coco & Ace of Hearts: C. scroll crafters."); } @@ -240,6 +243,7 @@ function writeFeatureTab_Project() { addFeature("Protected many flaws with login management system."); addFeature("Developed a robust anti-exploit login coordinator."); addFeature("Usage of HikariCP to improve DB connection calls."); + addFeature("Usage of Java Threadpool to improve runnable calls."); addFeature("Developed many survey tools for content profiling."); addFeature("ThreadTracker: runtime tool for deadlock detection."); addFeature("Channel, World and Server-wide timer management."); diff --git a/scripts/portal/enterDisguise1.js b/scripts/portal/enterDisguise1.js index ebc4163775..d7a5ddcab8 100644 --- a/scripts/portal/enterDisguise1.js +++ b/scripts/portal/enterDisguise1.js @@ -33,7 +33,7 @@ function enter(pi) { } if(pi.haveItem(4032101 + jobtype, 1)) { - pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); + pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight."); return false; } diff --git a/scripts/portal/enterDisguise2.js b/scripts/portal/enterDisguise2.js index 7eb70ac6bf..b242657cfc 100644 --- a/scripts/portal/enterDisguise2.js +++ b/scripts/portal/enterDisguise2.js @@ -33,7 +33,7 @@ function enter(pi) { } if(pi.haveItem(4032101 + jobtype, 1)) { - pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); + pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight."); return false; } diff --git a/scripts/portal/enterDisguise3.js b/scripts/portal/enterDisguise3.js index 06fad66aca..cf0b2c64a6 100644 --- a/scripts/portal/enterDisguise3.js +++ b/scripts/portal/enterDisguise3.js @@ -33,7 +33,7 @@ function enter(pi) { } if(pi.haveItem(4032101 + jobtype, 1)) { - pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); + pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight."); return false; } diff --git a/scripts/portal/enterDisguise4.js b/scripts/portal/enterDisguise4.js index 7b74961145..929887d851 100644 --- a/scripts/portal/enterDisguise4.js +++ b/scripts/portal/enterDisguise4.js @@ -33,7 +33,7 @@ function enter(pi) { } if(pi.haveItem(4032101 + jobtype, 1)) { - pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); + pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight."); return false; } diff --git a/scripts/portal/enterDisguise5.js b/scripts/portal/enterDisguise5.js index 8982871680..ddc080000a 100644 --- a/scripts/portal/enterDisguise5.js +++ b/scripts/portal/enterDisguise5.js @@ -33,7 +33,7 @@ function enter(pi) { } if(pi.haveItem(4032101 + jobtype, 1)) { - pi.message("You have already challenged the Master of Disguise, report your success to Neinheart."); + pi.message("You have already challenged the Master of Disguise, report your success to the Chief Knight."); return false; } diff --git a/scripts/portal/s4ship_out.js b/scripts/portal/s4ship_out.js new file mode 100644 index 0000000000..b24830505b --- /dev/null +++ b/scripts/portal/s4ship_out.js @@ -0,0 +1,32 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +function enter(pi) { + var exit = pi.getEventInstance().getIntProperty("canLeave"); + if (exit == 0) { + pi.message("You have to wait one minute before you can leave this place."); + return false; + } else if (exit == 2) { + pi.warp(912010200); + return true; + } else { + pi.warp(120000101); + return true; + } +} \ No newline at end of file diff --git a/scripts/portal/s4super_out.js b/scripts/portal/s4super_out.js new file mode 100644 index 0000000000..b24830505b --- /dev/null +++ b/scripts/portal/s4super_out.js @@ -0,0 +1,32 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +function enter(pi) { + var exit = pi.getEventInstance().getIntProperty("canLeave"); + if (exit == 0) { + pi.message("You have to wait one minute before you can leave this place."); + return false; + } else if (exit == 2) { + pi.warp(912010200); + return true; + } else { + pi.warp(120000101); + return true; + } +} \ No newline at end of file diff --git a/scripts/quest/21101.js b/scripts/quest/21101.js index d1a41c6b26..74815877db 100644 --- a/scripts/quest/21101.js +++ b/scripts/quest/21101.js @@ -20,6 +20,7 @@ along with this program. If not, see . */ importPackage(Packages.client); +importPackage(Packages.constants); status = -1; @@ -47,8 +48,10 @@ function start(mode, type, selection) { qm.changeJobById(2100); qm.resetStats(); - //qm.teachSkill(21000000, 0, 10, -1); //learned later... - //qm.teachSkill(21001003, 0, 20, -1); //learned later... + if (ServerConstants.USE_FULL_ARAN_SKILLSET) { + qm.teachSkill(21000000, 0, 10, -1); //combo ability + qm.teachSkill(21001003, 0, 20, -1); //polearm booster + } qm.completeQuest(); diff --git a/scripts/quest/21201.js b/scripts/quest/21201.js index e166b792e8..da8713a1cd 100644 --- a/scripts/quest/21201.js +++ b/scripts/quest/21201.js @@ -20,6 +20,7 @@ along with this program. If not, see . */ importPackage(Packages.client); +importPackage(Packages.constants); var status = -1; @@ -65,6 +66,13 @@ function end(mode, type, selection) { qm.gainItem(1142130, true); qm.changeJobById(2110); + + if (ServerConstants.USE_FULL_ARAN_SKILLSET) { + qm.teachSkill(21100000, 0, 20, -1); //polearm mastery + qm.teachSkill(21100002, 0, 30, -1); //final charge + qm.teachSkill(21100004, 0, 20, -1); //combo smash + qm.teachSkill(21100005, 0, 20, -1); //combo drain + } qm.completeQuest(); } diff --git a/scripts/quest/21302.js b/scripts/quest/21302.js index 46f6bc1f5d..0d3e60695c 100644 --- a/scripts/quest/21302.js +++ b/scripts/quest/21302.js @@ -20,6 +20,7 @@ along with this program. If not, see . */ importPackage(Packages.client); +importPackage(Packages.constants); var status = -1; @@ -49,6 +50,10 @@ function end(mode, type, selection) { qm.gainItem(1142131, true); qm.changeJobById(2111); + + if (ServerConstants.USE_FULL_ARAN_SKILLSET) { + qm.teachSkill(21110002, 0, 20, -1); //full swing + } qm.completeQuest(); } diff --git a/scripts/quest/3239.js b/scripts/quest/3239.js index a73a15fdf6..4e89e74cca 100644 --- a/scripts/quest/3239.js +++ b/scripts/quest/3239.js @@ -1,30 +1,45 @@ -importPackage(Packages.tools); +var status = -1; function end(mode, type, selection) { - var rnd; - - if (mode != 1) { + if (mode == -1) { qm.dispose(); } else { - if(qm.haveItem(4031092, 10)) { - if(qm.canHold(4031092)) { - qm.sendOk("Well done! You brought back all the #t4031092# that were missing. Here, get this scroll as a token of my gratitude..."); - qm.gainItem(4031092, -10); - - rnd = Math.floor(Math.random() * 4); - if(rnd == 0) qm.gainItem(2040704, 1); - else if(rnd == 1) qm.gainItem(2040705, 1); - else if(rnd == 2) qm.gainItem(2040707, 1); - else qm.gainItem(2040708, 1); - - qm.gainExp(2700); - qm.forceCompleteQuest(); + if(mode == 0 && type > 0) { + qm.dispose(); + return; + } + + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(qm.haveItem(4031092, 10)) { + if(qm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.USE).getNumFreeSlot() >= 1) { + qm.sendOk("Well done! You brought back all the #t4031092# that were missing. Here, take this scroll as a token of my gratitude..."); + } else { + qm.sendOk("Free a space on your USE inventory before receiving your prize."); + qm.dispose(); + return; } - else { - qm.sendOk("Free a space on your USE inventory before receiving your prize."); - } - } - - qm.dispose(); + } else { + qm.sendOk("Please return me 10 #t4031092# that went missing on this room."); + qm.dispose(); + return; + } + } else if (status == 1) { + qm.gainItem(4031092, -10); + + rnd = Math.floor(Math.random() * 4); + if(rnd == 0) qm.gainItem(2040704, 1); + else if(rnd == 1) qm.gainItem(2040705, 1); + else if(rnd == 2) qm.gainItem(2040707, 1); + else qm.gainItem(2040708, 1); + + qm.gainExp(2700); + qm.forceCompleteQuest(); + qm.dispose(); + } } } \ No newline at end of file diff --git a/scripts/quest/6030.js b/scripts/quest/6030.js index b82fe1822e..b9f4cfaa97 100644 --- a/scripts/quest/6030.js +++ b/scripts/quest/6030.js @@ -49,7 +49,7 @@ function end(mode, type, selection) { qm.sendNextPrev("And remember this: the maxima of #bExchange#k, the area of the fundamentals of Alchemy where the total amount of the material does not change, is that no item can be created from nothing. Understood?"); } else if (status == 5) { qm.gainMeso(-10000); - + qm.forceCompleteQuest(); qm.dispose(); } diff --git a/sql/db_drops.sql b/sql/db_drops.sql index 5c55b2292f..804a387284 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -19295,7 +19295,7 @@ USE `heavenms`; (4230116, 2382042, 1, 1, 0, 10000), (4230117, 2382055, 1, 1, 0, 10000), (4230118, 2382067, 1, 1, 0, 10000), -(4240000, 2383011, 1, 1, 0, 10000), +(4240000, 2383011, 1, 1, 0, 20000), (1210102, 4001354, 1, 1, 28209, 80000), (1110101, 4031773, 1, 1, 2145, 80000), (1130100, 4031773, 1, 1, 2145, 80000), @@ -20292,7 +20292,15 @@ USE `heavenms`; (9400110, 4000092, 1, 1, 0, 400000), (9400111, 4000093, 1, 1, 0, 400000), (3210201, 4031524, 1, 1, 8848, 200000), -(3110101, 4031527, 1, 1, 8849, 400000); +(3110101, 4031527, 1, 1, 8849, 400000), +(5100000, 2022001, 1, 1, 0, 20000), +(5100001, 2022001, 1, 1, 0, 20000), +(5130105, 2022001, 1, 1, 0, 20000), +(5130106, 2022001, 1, 1, 0, 20000), +(6130102, 2022001, 1, 1, 0, 20000), +(6130103, 2022001, 1, 1, 0, 20000), +(6130200, 2022001, 1, 1, 0, 20000), +(6130201, 2022001, 1, 1, 0, 20000); # (dropperid, itemid, minqty, maxqty, questid, chance) @@ -20478,7 +20486,8 @@ USE `heavenms`; (8142100, 2040205, 1, 1, 0, 750), (9400580, 2048010, 1, 1, 0, 750), (5110302, 2048010, 1, 1, 0, 750), -(4230118, 4240000, 1, 1, 0, 750), +(4230118, 2048011, 1, 1, 0, 750), +(4240000, 2048011, 1, 1, 0, 750), (6400100, 2048011, 1, 1, 0, 750), (5130107, 2048012, 1, 1, 0, 750), (6230601, 2048012, 1, 1, 0, 750), diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index ce77bd2b7a..5f1f93ab9d 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -72,6 +72,7 @@ import server.MapleStatEffect; import server.MapleStorage; import server.MapleTrade; import server.TimerManager; +import server.ThreadManager; import server.events.MapleEvents; import server.events.RescueGaga; import server.events.gm.MapleFitness; @@ -109,6 +110,7 @@ import tools.packets.Wedding; import client.autoban.AutobanManager; import client.creator.CharacterFactoryRecipe; import client.inventory.Equip; +import client.inventory.Equip.StatUpgrade; import client.inventory.Item; import client.inventory.ItemFactory; import client.inventory.MapleInventory; @@ -159,12 +161,10 @@ import server.life.MobSkillFactory; import server.maps.MapleMapItem; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; -import server.movement.AbsoluteLifeMovement; -import server.movement.LifeMovementFragment; public class MapleCharacter extends AbstractMapleCharacterObject { private static final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); - private static final String LEVEL_200 = "[Congrats] %s has reached Level 200! Congratulate %s on such an amazing achievement!"; + private static final String LEVEL_200 = "[Congrats] %s has reached Level %d! Congratulate %s on such an amazing achievement!"; private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "help", "helper", "alert", "notice", "maplestory", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay", "nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm", "operate", "master", "sysop", "party", "GameMaster", "community", "message", "event", "test", "meso", "Scania", "yata", "AsiaSoft", "henesys"}; @@ -853,10 +853,15 @@ public class MapleCharacter extends AbstractMapleCharacterObject { List> ldsstat = Collections.singletonList(new Pair(MapleBuffStat.DARKSIGHT, 0)); getMap().broadcastGMMessage(this, MaplePacketCreator.giveForeignBuff(id, ldsstat), false); for (MapleMonster mon : this.getControlledMonsters()) { - mon.setController(null); - mon.setControllerHasAggro(false); - mon.setControllerKnowsAboutAggro(false); - mon.getMap().updateMonsterController(mon); + mon.lockMonster(); + try { + mon.setController(null); + mon.setControllerHasAggro(false); + mon.setControllerKnowsAboutAggro(false); + mon.getMap().updateMonsterController(mon); + } finally { + mon.unlockMonster(); + } } } announce(MaplePacketCreator.enableActions()); @@ -1178,11 +1183,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void broadcastStance() { - AbsoluteLifeMovement alm = new AbsoluteLifeMovement(0, getPosition(), 0, getStance()); - alm.setPixelsPerSecond(new Point(0, 0)); - List moveUpdate = Collections.singletonList((LifeMovementFragment) alm); - - map.broadcastMessage(this, MaplePacketCreator.movePlayer(id, moveUpdate), false); + map.broadcastMessage(this, MaplePacketCreator.movePlayer(id, this.getIdleMovement()), false); } public MapleMap getWarpMap(int map) { @@ -1592,11 +1593,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { map.addPlayer(this); visitMap(map); + silentPartyUpdateInternal(getParty()); // EIM script calls inside prtLock.lock(); try { if (party != null) { mpc.setMapId(to.getId()); - silentPartyUpdateInternal(); client.announce(MaplePacketCreator.updateParty(client.getChannel(), party, PartyOperation.SILENT_UPDATE, null)); updatePartyMemberHPInternal(); } @@ -1605,8 +1606,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } if (getMap().getHPDec() > 0) resetHpDecreaseTask(); - } - else { + } else { FilePrinter.printError(FilePrinter.MAPLE_MAP, "Character " + this.getName() + " got stuck when moving to map " + map.getId() + "."); } @@ -1708,19 +1708,29 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void checkMonsterAggro(MapleMonster monster) { - if (!monster.isControllerHasAggro()) { - if (monster.getController() == this) { - monster.setControllerHasAggro(true); - } else { - monster.switchController(this, true); + monster.lockMonster(); + try { + if (!monster.isControllerHasAggro()) { + if (monster.getController() == this) { + monster.setControllerHasAggro(true); + } else { + monster.switchController(this, true); + } } + } finally { + monster.unlockMonster(); } } public void controlMonster(MapleMonster monster, boolean aggro) { - monster.setController(this); - controlled.add(monster); - client.announce(MaplePacketCreator.controlMonster(monster, false, aggro)); + monster.lockMonster(); + try { + monster.setController(this); + controlled.add(monster); + client.announce(MaplePacketCreator.controlMonster(monster, false, aggro)); + } finally { + monster.unlockMonster(); + } } private boolean useItem(final int id) { @@ -1768,7 +1778,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (ob instanceof MapleMapItem) { MapleMapItem mapitem = (MapleMapItem) ob; - if (System.currentTimeMillis() - mapitem.getDropTime() < 900 || !mapitem.canBePickedBy(this)) { + if (System.currentTimeMillis() - mapitem.getDropTime() < 400 || !mapitem.canBePickedBy(this)) { client.announce(MaplePacketCreator.enableActions()); return; } @@ -1778,6 +1788,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { mpcs = getPartyMembersOnSameMap(); } + String itemScriptName = null; mapitem.lockItem(); try { if (mapitem.isPickedUp()) { @@ -1852,12 +1863,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } else if (mItem.getItemId() / 10000 == 243) { MapleItemInformationProvider.scriptedItem info = ii.getScriptedItemInfo(mItem.getItemId()); if (info.runOnPickup()) { - ItemScriptManager ism = ItemScriptManager.getInstance(); - String scriptName = info.getScript(); - if (ism.scriptExists(scriptName)) { - ism.getItemScript(client, scriptName); - } - + itemScriptName = info.getScript(); } else { if (!MapleInventoryManipulator.addFromDrop(client, mItem, true)) { client.announce(MaplePacketCreator.enableActions()); @@ -1890,6 +1896,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } finally { mapitem.unlockItem(); } + + if (itemScriptName != null) { + ItemScriptManager ism = ItemScriptManager.getInstance(); + if (ism.scriptExists(itemScriptName)) { + ism.runItemScript(client, itemScriptName); + } + } } client.announce(MaplePacketCreator.enableActions()); } @@ -2913,16 +2926,21 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } - private synchronized int applyFame(int delta) { - int newFame = fame + delta; - if (newFame < -30000) { - delta = -(30000 + fame); - } else if (newFame > 30000) { - delta = 30000 - fame; + private Pair applyFame(int delta) { + petLock.lock(); + try { + int newFame = fame + delta; + if (newFame < -30000) { + delta = -(30000 + fame); + } else if (newFame > 30000) { + delta = 30000 - fame; + } + + fame += delta; + return new Pair<>(fame, delta); + } finally { + petLock.unlock(); } - - fame += delta; - return delta; } public void gainFame(int delta) { @@ -2930,9 +2948,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public boolean gainFame(int delta, MapleCharacter fromPlayer, int mode) { - delta = applyFame(delta); + Pair fameRes = applyFame(delta); + delta = fameRes.getRight(); if (delta != 0) { - int thisFame = fame; + int thisFame = fameRes.getLeft(); updateSingleStat(MapleStat.FAME, thisFame); if (fromPlayer != null) { @@ -3571,10 +3590,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject { for (MapleCharacter chr : townChars) { destroyDoor.getTownDoor().sendDestroyData(chr.getClient()); } - if(destroyDoor.getTownPortal().getId() == 0x80) { + if (destroyDoor.getTownPortal().getId() == 0x80) { for (MapleCharacter chr : townChars) { MapleDoor door = chr.getMainTownDoor(); - if(door != null) { + if (door != null) { destroyDoor.getTownDoor().sendSpawnData(chr.getClient()); } } @@ -4180,29 +4199,33 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void applyPartyDoor(MapleDoor door, boolean partyUpdate) { + MapleParty chrParty; prtLock.lock(); try { if (!partyUpdate) { pdoor = door; } - if (party != null) { - party.addDoor(id, door); - silentPartyUpdateInternal(); + chrParty = getParty(); + if (chrParty != null) { + chrParty.addDoor(id, door); } } finally { prtLock.unlock(); } + + silentPartyUpdateInternal(chrParty); } private MapleDoor removePartyDoor(boolean partyUpdate) { MapleDoor ret = null; + MapleParty chrParty; prtLock.lock(); try { - if (party != null) { - party.removeDoor(id); - silentPartyUpdateInternal(); + chrParty = getParty(); + if (chrParty != null) { + chrParty.removeDoor(id); } if (!partyUpdate) { @@ -4213,6 +4236,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { prtLock.unlock(); } + silentPartyUpdateInternal(chrParty); return ret; } @@ -5796,18 +5820,16 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (level == maxClassLevel) { if (!this.isGM()) { if (ServerConstants.PLAYERNPC_AUTODEPLOY) { - Thread t = new Thread(new Runnable() { + ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), MapleCharacter.this); } }); - - t.start(); } final String names = (getMedalText() + name); - getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, String.format(LEVEL_200, names, names))); + getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, String.format(LEVEL_200, names, maxClassLevel, names))); } } @@ -5874,22 +5896,27 @@ public class MapleCharacter extends AbstractMapleCharacterObject { Runnable r = new Runnable() { @Override public void run() { + MapleParty party; + boolean partyLeader; + prtLock.lock(); try { - if (party != null) { - if(isPartyLeader()) party.assignNewLeader(client); - PartyOperationHandler.leaveParty(party, mpc, client); - - showHint("You have reached #blevel 10#k, therefore you must leave your #rstarter party#k."); - } + party = getParty(); + partyLeader = party != null && isPartyLeader(); } finally { prtLock.unlock(); } + + if (party != null) { + if(partyLeader) party.assignNewLeader(client); + PartyOperationHandler.leaveParty(party, mpc, client); + + showHint("You have reached #blevel 10#k, therefore you must leave your #rstarter party#k."); + } } }; - Thread t = new Thread(r); - t.start(); + ThreadManager.getInstance().newTask(r); } levelUpMessages(); @@ -7567,8 +7594,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } }; - Thread t = new Thread(r); //spawns a new thread to deal with this - t.start(); + ThreadManager.getInstance().newTask(r); //spawns a new thread to deal with this } else { saveCharToDB(true); } @@ -8439,23 +8465,34 @@ public class MapleCharacter extends AbstractMapleCharacterObject { public int sellAllItemsFromName(byte invTypeId, String name) { //player decides from which inventory items should be sold. - MapleInventoryType type = MapleInventoryType.getByType(invTypeId); - Item it = getInventory(type).findByName(name); - if(it == null) { - return(-1); + MapleInventory inv = getInventory(type); + inv.lockInventory(); + try { + Item it = inv.findByName(name); + if(it == null) { + return(-1); + } + + return(sellAllItemsFromPosition(ii, type, it.getPosition())); + } finally { + inv.unlockInventory(); } - - return(sellAllItemsFromPosition(ii, type, it.getPosition())); } public int sellAllItemsFromPosition(MapleItemInformationProvider ii, MapleInventoryType type, short pos) { int mesoGain = 0; - for(short i = pos; i <= getInventory(type).getSlotLimit(); i++) { - if(getInventory(type).getItem(i) == null) continue; - mesoGain += standaloneSell(getClient(), ii, type, i, getInventory(type).getItem(i).getQuantity()); + MapleInventory inv = getInventory(type); + inv.lockInventory(); + try { + for(short i = pos; i <= inv.getSlotLimit(); i++) { + if(inv.getItem(i) == null) continue; + mesoGain += standaloneSell(getClient(), ii, type, i, inv.getItem(i).getQuantity()); + } + } finally { + inv.unlockInventory(); } return(mesoGain); @@ -8465,36 +8502,199 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (quantity == 0xFFFF || quantity == 0) { quantity = 1; } - Item item = getInventory(type).getItem((short) slot); - if (item == null){ //Basic check - return(0); - } - int itemid = item.getItemId(); - if (ItemConstants.isRechargeable(itemid)) { - quantity = item.getQuantity(); - } else if (ItemConstants.isWeddingToken(itemid) || ItemConstants.isWeddingRing(itemid)) { + MapleInventory inv = getInventory(type); + inv.lockInventory(); + try { + Item item = inv.getItem((short) slot); + if (item == null){ //Basic check + return(0); + } + + int itemid = item.getItemId(); + if (ItemConstants.isRechargeable(itemid)) { + quantity = item.getQuantity(); + } else if (ItemConstants.isWeddingToken(itemid) || ItemConstants.isWeddingRing(itemid)) { + return(0); + } + + if (quantity < 0) { + return(0); + } + short iQuant = item.getQuantity(); + if (iQuant == 0xFFFF) { + iQuant = 1; + } + + if (quantity <= iQuant && iQuant > 0) { + MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false); + int recvMesos = ii.getPrice(itemid, quantity); + if (recvMesos > 0) { + gainMeso(recvMesos, false); + return(recvMesos); + } + } + return(0); + } finally { + inv.unlockInventory(); } + } + + private static boolean hasMergeFlag(Item item) { + return (item.getFlag() & ItemConstants.MERGE_UNTRADEABLE) == ItemConstants.MERGE_UNTRADEABLE; + } + + private static void setMergeFlag(Item item) { + int flag = item.getFlag(); + flag |= ItemConstants.MERGE_UNTRADEABLE; + flag |= ItemConstants.UNTRADEABLE; + item.setFlag((byte) flag); + } + + private List getUpgradeableEquipped() { + List list = new LinkedList<>(); - if (quantity < 0) { - return(0); - } - short iQuant = item.getQuantity(); - if (iQuant == 0xFFFF) { - iQuant = 1; - } - - if (quantity <= iQuant && iQuant > 0) { - MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false); - int recvMesos = ii.getPrice(itemid, quantity); - if (recvMesos > 0) { - gainMeso(recvMesos, false); - return(recvMesos); + for (Item item : getInventory(MapleInventoryType.EQUIPPED)) { + if (ii.isUpgradeable(item.getItemId())) { + list.add((Equip) item); } } - return(0); + return list; + } + + private static List getEquipsWithStat(List>> equipped, StatUpgrade stat) { + List equippedWithStat = new LinkedList<>(); + + for (Pair> eq : equipped) { + if (eq.getRight().containsKey(stat)) { + equippedWithStat.add(eq.getLeft()); + } + } + + return equippedWithStat; + } + + public boolean mergeAllItemsFromName(String name) { + MapleInventoryType type = MapleInventoryType.EQUIP; + + MapleInventory inv = getInventory(type); + inv.lockInventory(); + try { + Item it = inv.findByName(name); + if(it == null) { + return false; + } + + Map statups = new LinkedHashMap<>(); + mergeAllItemsFromPosition(statups, it.getPosition()); + + List>> upgradeableEquipped = new LinkedList<>(); + Map>> equipUpgrades = new LinkedHashMap<>(); + for (Equip eq : getUpgradeableEquipped()) { + upgradeableEquipped.add(new Pair<>(eq, eq.getStats())); + equipUpgrades.put(eq, new LinkedList>()); + } + + /* + for (Entry e : statups.entrySet()) { + System.out.println(e); + } + */ + + for (Entry e : statups.entrySet()) { + Double ev = Math.sqrt(e.getValue()); + + Set extraEquipped = new LinkedHashSet<>(equipUpgrades.keySet()); + List statEquipped = getEquipsWithStat(upgradeableEquipped, e.getKey()); + float extraRate = (float)(0.2 * Math.random()); + + if (!statEquipped.isEmpty()) { + float statRate = 1.0f - extraRate; + + int statup = (int) Math.ceil((ev * statRate) / statEquipped.size()); + for (Equip statEq : statEquipped) { + equipUpgrades.get(statEq).add(new Pair<>(e.getKey(), statup)); + extraEquipped.remove(statEq); + } + } + + if (!extraEquipped.isEmpty()) { + int statup = (int) Math.round((ev * extraRate) / extraEquipped.size()); + if (statup > 0) { + for (Equip extraEq : extraEquipped) { + equipUpgrades.get(extraEq).add(new Pair<>(e.getKey(), statup)); + } + } + } + } + + dropMessage(6, "EQUIPMENT MERGE operation results:"); + for (Entry>> eqpUpg : equipUpgrades.entrySet()) { + List> eqpStatups = eqpUpg.getValue(); + if (!eqpStatups.isEmpty()) { + Equip eqp = eqpUpg.getKey(); + setMergeFlag(eqp); + + String showStr = " '" + MapleItemInformationProvider.getInstance().getName(eqp.getItemId()) + "': "; + String upgdStr = eqp.gainStats(eqpStatups).getLeft(); + + this.forceUpdateItem(eqp); + + showStr += upgdStr; + dropMessage(6, showStr); + } + } + + return true; + } finally { + inv.unlockInventory(); + } + } + + public void mergeAllItemsFromPosition(Map statups, short pos) { + MapleInventory inv = getInventory(MapleInventoryType.EQUIP); + inv.lockInventory(); + try { + for(short i = pos; i <= inv.getSlotLimit(); i++) { + standaloneMerge(statups, getClient(), MapleInventoryType.EQUIP, i, inv.getItem(i)); + } + } finally { + inv.unlockInventory(); + } + } + + private void standaloneMerge(Map statups, MapleClient c, MapleInventoryType type, short slot, Item item) { + short quantity; + if (item == null || (quantity = item.getQuantity()) < 1 || ii.isCash(item.getItemId()) || !ii.isUpgradeable(item.getItemId()) || hasMergeFlag(item)){ + return; + } + + Equip e = (Equip) item; + for (Entry s : e.getStats().entrySet()) { + Float newVal = statups.get(s.getKey()); + + float incVal = s.getValue().floatValue(); + switch (s.getKey()) { + case incPAD: + case incMAD: + case incPDD: + case incMDD: + incVal = (float) Math.log(incVal); + break; + } + + if (newVal != null) { + newVal += incVal; + } else { + newVal = incVal; + } + + statups.put(s.getKey(), newVal); + } + + MapleInventoryManipulator.removeFromSlot(c, type, (byte) slot, quantity, false); } public void setShop(MapleShop shop) { @@ -8599,17 +8799,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } public void silentPartyUpdate() { - prtLock.lock(); - try { - silentPartyUpdateInternal(); - } finally { - prtLock.unlock(); - } + silentPartyUpdateInternal(getParty()); } - private void silentPartyUpdateInternal() { - if (party != null) { - getWorldServer().updateParty(party.getId(), PartyOperation.SILENT_UPDATE, getMPC()); + private void silentPartyUpdateInternal(MapleParty chrParty) { + if (chrParty != null) { + getWorldServer().updateParty(chrParty.getId(), PartyOperation.SILENT_UPDATE, getMPC()); } } @@ -8662,7 +8857,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { } } - public void runTirednessSchedule() { + public boolean runTirednessSchedule() { if(maplemount != null) { int tiredness = maplemount.incrementAndGetTiredness(); @@ -8671,8 +8866,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject { maplemount.setTiredness(99); this.dispelSkill(this.getJobType() * 10000000 + 1004); this.dropMessage(6, "Your mount grew tired! Treat it some revitalizer before riding it again!"); + return false; } } + + return true; } public void startMapEffect(String msg, int itemId) { @@ -8956,7 +9154,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { if (!this.isHidden() || client.getPlayer().gmLevel() > 1) { client.announce(MaplePacketCreator.spawnPlayerMapObject(client, this, false)); - if(hasBuffFromSourceid(getJobMapChair(job))) { + if (buffEffects.containsKey(getJobMapChair(job))) { // mustn't effLock, chrLock this function client.announce(MaplePacketCreator.giveForeignChairSkillEffect(id)); } } diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index d719989a47..341577b9f5 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -74,6 +74,7 @@ import scripting.quest.QuestActionManager; import scripting.quest.QuestScriptManager; import server.life.MapleMonster; import server.MapleTrade; +import server.ThreadManager; import server.maps.*; import server.quest.MapleQuest; @@ -159,7 +160,7 @@ public class MapleClient { } public void sendCharList(int server) { - this.announce(MaplePacketCreator.getCharList(this, server)); + this.announce(MaplePacketCreator.getCharList(this, server, 0)); } public List loadCharacters(int serverId) { @@ -878,11 +879,33 @@ public class MapleClient { } } - public final synchronized void disconnect(boolean shutdown, boolean cashshop) {//once per MapleClient instance + public final void disconnect(final boolean shutdown, final boolean cashshop) { + if (isDisconnecting()) { + ThreadManager.getInstance().newTask(new Runnable() { + @Override + public void run() { + disconnectInternal(shutdown, cashshop); + } + }); + } + } + + public final void forceDisconnect() { + if (isDisconnecting()) { + disconnectInternal(true, false); + } + } + + private synchronized boolean isDisconnecting() { if (disconnecting) { - return; + return false; } + disconnecting = true; + return true; + } + + private void disconnectInternal(boolean shutdown, boolean cashshop) {//once per MapleClient instance if (player != null && player.isLoggedin() && player.getClient() != null) { final int messengerid = player.getMessenger() == null ? 0 : player.getMessenger().getId(); //final int fid = player.getFamilyId(); diff --git a/src/client/autoban/AutobanManager.java b/src/client/autoban/AutobanManager.java index ac27915192..1180411754 100644 --- a/src/client/autoban/AutobanManager.java +++ b/src/client/autoban/AutobanManager.java @@ -6,6 +6,7 @@ package client.autoban; import client.MapleCharacter; +import constants.ServerConstants; import java.util.HashMap; import java.util.Map; import net.server.Server; @@ -109,11 +110,15 @@ public class AutobanManager { if (this.timestamp[type] == time) { this.timestampcounter[type]++; if (this.timestampcounter[type] >= times) { - chr.getClient().disconnect(false, false); + if (ServerConstants.USE_AUTOBAN) { + chr.getClient().disconnect(false, false); + } + FilePrinter.print(FilePrinter.EXPLOITS, "Player " + chr + " was caught spamming TYPE " + type + " and has been disconnected."); } return; } this.timestamp[type] = time; + this.timestampcounter[type] = 0; } } diff --git a/src/client/command/commands/gm0/TimeCommand.java b/src/client/command/commands/gm0/TimeCommand.java index 08f862b90c..9e4941dcf6 100644 --- a/src/client/command/commands/gm0/TimeCommand.java +++ b/src/client/command/commands/gm0/TimeCommand.java @@ -40,7 +40,7 @@ public class TimeCommand extends Command { @Override public void execute(MapleClient client, String[] params) { DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); client.getPlayer().yellowMessage("HeavenMS Server Time: " + dateFormat.format(new Date())); } } diff --git a/src/client/command/commands/gm2/SummonCommand.java b/src/client/command/commands/gm2/SummonCommand.java index c3bd50dec4..591d44b3b2 100644 --- a/src/client/command/commands/gm2/SummonCommand.java +++ b/src/client/command/commands/gm2/SummonCommand.java @@ -43,7 +43,9 @@ public class SummonCommand extends Command { } MapleCharacter victim = c.getChannelServer().getPlayerStorage().getCharacterByName(params[0]); - if (victim == null) {//If victim isn't on current channel, loop all channels on current world. + if (victim == null) { + //If victim isn't on current channel, loop all channels on current world. + for (Channel ch : Server.getInstance().getChannelsFromWorld(c.getWorld())) { victim = ch.getPlayerStorage().getCharacterByName(params[0]); if (victim != null) { @@ -61,9 +63,10 @@ public class SummonCommand extends Command { victim.getEventInstance().unregisterPlayer(victim); } } + //Attempt to join the warpers instance. if (player.getEventInstance() != null && changingEvent) { - if (player.getClient().getChannel() == victim.getClient().getChannel()) {//just in case.. you never know... + if (player.getClient().getChannel() == victim.getClient().getChannel()) { player.getEventInstance().registerPlayer(victim); victim.changeMap(player.getEventInstance().getMapInstance(player.getMapId()), player.getMap().findClosestPortal(player.getPosition())); } else { diff --git a/src/client/command/commands/gm5/DebugCommand.java b/src/client/command/commands/gm5/DebugCommand.java index 1bc4e0f8bc..43d1fc56f7 100644 --- a/src/client/command/commands/gm5/DebugCommand.java +++ b/src/client/command/commands/gm5/DebugCommand.java @@ -71,7 +71,8 @@ public class DebugCommand extends Command { List monsters = player.getMap().getMapObjectsInRange(player.getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER)); for (MapleMapObject monstermo : monsters) { MapleMonster monster = (MapleMonster) monstermo; - player.message("Monster ID: " + monster.getId() + " Aggro target: " + ((monster.getController() != null) ? monster.getController().getName() : "")); + MapleCharacter controller = monster.getController(); + player.message("Monster ID: " + monster.getId() + " Aggro target: " + ((controller != null) ? controller.getName() : "")); } break; diff --git a/src/client/command/commands/gm6/ServerAddChannelCommand.java b/src/client/command/commands/gm6/ServerAddChannelCommand.java index a9055f2ab8..34edf2ae0e 100644 --- a/src/client/command/commands/gm6/ServerAddChannelCommand.java +++ b/src/client/command/commands/gm6/ServerAddChannelCommand.java @@ -27,6 +27,7 @@ import client.MapleCharacter; import client.command.Command; import client.MapleClient; import net.server.Server; +import server.ThreadManager; public class ServerAddChannelCommand extends Command { { @@ -44,7 +45,7 @@ public class ServerAddChannelCommand extends Command { final int worldid = Integer.parseInt(params[0]); - new Thread(new Runnable() { + ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { int chid = Server.getInstance().addChannel(worldid); @@ -64,6 +65,6 @@ public class ServerAddChannelCommand extends Command { } } } - }).start(); + }); } } diff --git a/src/client/command/commands/gm6/ServerAddWorldCommand.java b/src/client/command/commands/gm6/ServerAddWorldCommand.java index 00aae016d5..98074e2628 100644 --- a/src/client/command/commands/gm6/ServerAddWorldCommand.java +++ b/src/client/command/commands/gm6/ServerAddWorldCommand.java @@ -27,6 +27,7 @@ import client.MapleCharacter; import client.command.Command; import client.MapleClient; import net.server.Server; +import server.ThreadManager; public class ServerAddWorldCommand extends Command { { @@ -37,7 +38,7 @@ public class ServerAddWorldCommand extends Command { public void execute(MapleClient c, String[] params) { final MapleCharacter player = c.getPlayer(); - new Thread(new Runnable() { + ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { int wid = Server.getInstance().addWorld(); @@ -54,6 +55,6 @@ public class ServerAddWorldCommand extends Command { } } } - }).start(); + }); } } diff --git a/src/client/command/commands/gm6/ServerRemoveChannelCommand.java b/src/client/command/commands/gm6/ServerRemoveChannelCommand.java index 09385dbd49..0c8112e6e4 100644 --- a/src/client/command/commands/gm6/ServerRemoveChannelCommand.java +++ b/src/client/command/commands/gm6/ServerRemoveChannelCommand.java @@ -27,6 +27,7 @@ import client.MapleCharacter; import client.command.Command; import client.MapleClient; import net.server.Server; +import server.ThreadManager; public class ServerRemoveChannelCommand extends Command { { @@ -43,7 +44,7 @@ public class ServerRemoveChannelCommand extends Command { } final int worldId = Integer.parseInt(params[0]); - new Thread(new Runnable() { + ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { if(Server.getInstance().removeChannel(worldId)) { @@ -56,6 +57,6 @@ public class ServerRemoveChannelCommand extends Command { } } } - }).start(); + }); } } diff --git a/src/client/command/commands/gm6/ServerRemoveWorldCommand.java b/src/client/command/commands/gm6/ServerRemoveWorldCommand.java index b298965c7e..35acf28b47 100644 --- a/src/client/command/commands/gm6/ServerRemoveWorldCommand.java +++ b/src/client/command/commands/gm6/ServerRemoveWorldCommand.java @@ -27,6 +27,7 @@ import client.MapleCharacter; import client.command.Command; import client.MapleClient; import net.server.Server; +import server.ThreadManager; public class ServerRemoveWorldCommand extends Command { { @@ -43,7 +44,7 @@ public class ServerRemoveWorldCommand extends Command { return; } - new Thread(new Runnable() { + ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { if(Server.getInstance().removeWorld()) { @@ -60,6 +61,6 @@ public class ServerRemoveWorldCommand extends Command { } } } - }).start(); + }); } } diff --git a/src/client/inventory/Equip.java b/src/client/inventory/Equip.java index 307c7bd221..2959774f18 100644 --- a/src/client/inventory/Equip.java +++ b/src/client/inventory/Equip.java @@ -24,8 +24,10 @@ package client.inventory; import client.MapleClient; import constants.ServerConstants; import constants.ExpTable; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import server.MapleItemInformationProvider; import tools.MaplePacketCreator; import tools.Pair; @@ -47,7 +49,7 @@ public class Equip extends Item { } } - private static enum StatUpgrade { + public static enum StatUpgrade { incDEX(0), incSTR(1), incINT(2), incLUK(3), incMHP(4), incMMP(5), incPAD(6), incMAD(7), @@ -332,49 +334,30 @@ public class Equip extends Item { if(jump > 0) getUnitStatUpgrade(stats, StatUpgrade.incJump, jump, false); } - private void gainLevel(MapleClient c) { - List> stats = new LinkedList<>(); - - if(isElemental) { - List> elementalStats = MapleItemInformationProvider.getInstance().getItemLevelupStats(getItemId(), itemLevel); - - for(Pair p: elementalStats) { - if(p.getRight() > 0) stats.add(new Pair<>(StatUpgrade.valueOf(p.getLeft()), p.getRight())); - } - } + public Map getStats() { + Map stats = new HashMap<>(5); - if(!stats.isEmpty()) { - if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { - if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); - getUnitSlotUpgrade(stats, StatUpgrade.incSlot); - } - } - else { - isUpgradeable = false; - - improveDefaultStats(stats); - if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { - if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); - getUnitSlotUpgrade(stats, StatUpgrade.incSlot); - } - - if(isUpgradeable) { - while(stats.isEmpty()) { - improveDefaultStats(stats); - if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { - if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); - getUnitSlotUpgrade(stats, StatUpgrade.incSlot); - } - } - } - } - - itemLevel++; - boolean gotVicious = false, gotSlot = false; - - String lvupStr = "'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "' is now level " + itemLevel + "! "; - String showStr = "#e'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "'#b is now #elevel #r" + itemLevel + "#k#b!"; + if(dex > 0) stats.put(StatUpgrade.incDEX, dex); + if(str > 0) stats.put(StatUpgrade.incSTR, str); + if(_int > 0) stats.put(StatUpgrade.incINT,_int); + if(luk > 0) stats.put(StatUpgrade.incLUK, luk); + if(hp > 0) stats.put(StatUpgrade.incMHP, hp); + if(mp > 0) stats.put(StatUpgrade.incMMP, mp); + if(watk > 0) stats.put(StatUpgrade.incPAD, watk); + if(matk > 0) stats.put(StatUpgrade.incMAD, matk); + if(wdef > 0) stats.put(StatUpgrade.incPDD, wdef); + if(mdef > 0) stats.put(StatUpgrade.incMDD, mdef); + if(avoid > 0) stats.put(StatUpgrade.incEVA, avoid); + if(acc > 0) stats.put(StatUpgrade.incACC, acc); + if(speed > 0) stats.put(StatUpgrade.incSpeed, speed); + if(jump > 0) stats.put(StatUpgrade.incJump, jump); + return stats; + } + + public Pair> gainStats(List> stats) { + boolean gotSlot = false, gotVicious = false; + String lvupStr = ""; Integer statUp, maxStat = ServerConstants.MAX_EQUIPMNT_STAT; for (Pair stat : stats) { switch (stat.getLeft()) { @@ -460,6 +443,55 @@ public class Equip extends Item { } } + return new Pair<>(lvupStr, new Pair<>(gotSlot, gotVicious)); + } + + private void gainLevel(MapleClient c) { + List> stats = new LinkedList<>(); + + if(isElemental) { + List> elementalStats = MapleItemInformationProvider.getInstance().getItemLevelupStats(getItemId(), itemLevel); + + for(Pair p: elementalStats) { + if(p.getRight() > 0) stats.add(new Pair<>(StatUpgrade.valueOf(p.getLeft()), p.getRight())); + } + } + + if(!stats.isEmpty()) { + if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { + if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); + getUnitSlotUpgrade(stats, StatUpgrade.incSlot); + } + } else { + isUpgradeable = false; + + improveDefaultStats(stats); + if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { + if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); + getUnitSlotUpgrade(stats, StatUpgrade.incSlot); + } + + if(isUpgradeable) { + while(stats.isEmpty()) { + improveDefaultStats(stats); + if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) { + if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious); + getUnitSlotUpgrade(stats, StatUpgrade.incSlot); + } + } + } + } + + itemLevel++; + + String lvupStr = "'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "' is now level " + itemLevel + "! "; + String showStr = "#e'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "'#b is now #elevel #r" + itemLevel + "#k#b!"; + + Pair> res = gainStats(stats); + lvupStr += res.getLeft(); + boolean gotSlot = res.getRight().getLeft(); + boolean gotVicious = res.getRight().getRight(); + if(gotVicious) { //c.getPlayer().dropMessage(6, "A new Vicious Hammer opportunity has been found on the '" + MapleItemInformationProvider.getInstance().getName(getItemId()) + "'!"); lvupStr += "+VICIOUS "; diff --git a/src/client/inventory/Item.java b/src/client/inventory/Item.java index c9b8691dd1..8173dd5b1d 100644 --- a/src/client/inventory/Item.java +++ b/src/client/inventory/Item.java @@ -73,6 +73,7 @@ public class Item implements Comparable { public void setPosition(short position) { this.position = position; + if (this.pet != null) this.pet.setPosition(position); } public void setQuantity(short quantity) { diff --git a/src/client/inventory/MaplePet.java b/src/client/inventory/MaplePet.java index 8f320e5565..2f9b8d3fd0 100644 --- a/src/client/inventory/MaplePet.java +++ b/src/client/inventory/MaplePet.java @@ -215,7 +215,7 @@ public class MaplePet extends Item { enjoyed = false; } - owner.getMap().broadcastMessage(MaplePacketCreator.commandResponse(owner.getId(), slot, type, enjoyed)); + owner.getMap().broadcastMessage(MaplePacketCreator.petFoodResponse(owner.getId(), slot, enjoyed, false)); saveToDb(); Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition()); diff --git a/src/client/inventory/manipulator/MapleInventoryManipulator.java b/src/client/inventory/manipulator/MapleInventoryManipulator.java index e26f36824a..c70d133bfb 100644 --- a/src/client/inventory/manipulator/MapleInventoryManipulator.java +++ b/src/client/inventory/manipulator/MapleInventoryManipulator.java @@ -188,6 +188,7 @@ public class MapleInventoryManipulator { short newQ = (short) Math.min(oldQ + quantity, slotMax); quantity -= (newQ - oldQ); eItem.setQuantity(newQ); + item.setPosition(eItem.getPosition()); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(1, eItem)))); } } else { @@ -209,6 +210,8 @@ public class MapleInventoryManipulator { item.setQuantity((short) (quantity + newQ)); return false; } + nItem.setPosition(newSlot); + item.setPosition(newSlot); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem)))); if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem(); } @@ -223,6 +226,8 @@ public class MapleInventoryManipulator { c.announce(MaplePacketCreator.getShowInventoryFull()); return false; } + nItem.setPosition(newSlot); + item.setPosition(newSlot); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem)))); if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem(); c.announce(MaplePacketCreator.enableActions()); @@ -234,6 +239,7 @@ public class MapleInventoryManipulator { c.announce(MaplePacketCreator.getShowInventoryFull()); return false; } + item.setPosition(newSlot); c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item)))); if(MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem(); } else { diff --git a/src/client/processor/AssignAPProcessor.java b/src/client/processor/AssignAPProcessor.java index 801816ac33..c563bbc49c 100644 --- a/src/client/processor/AssignAPProcessor.java +++ b/src/client/processor/AssignAPProcessor.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import server.ThreadManager; import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; @@ -333,13 +334,12 @@ public class AssignAPProcessor { AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign."); final MapleClient client = c; - Thread t = new Thread(new Runnable() { + ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { client.disconnect(false, false); } }); - t.start(); return; } diff --git a/src/client/processor/AssignSPProcessor.java b/src/client/processor/AssignSPProcessor.java index 979f1439ba..7c8d868a88 100644 --- a/src/client/processor/AssignSPProcessor.java +++ b/src/client/processor/AssignSPProcessor.java @@ -30,6 +30,7 @@ import client.SkillFactory; import client.autoban.AutobanFactory; import constants.GameConstants; import constants.skills.Aran; +import server.ThreadManager; import tools.FilePrinter; import tools.MaplePacketCreator; @@ -55,13 +56,12 @@ public class AssignSPProcessor { FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n"); final MapleClient client = c; - Thread t = new Thread(new Runnable() { + ThreadManager.getInstance().newTask(new Runnable() { @Override public void run() { client.disconnect(true, false); } }); - t.start(); return; } diff --git a/src/client/processor/MakerProcessor.java b/src/client/processor/MakerProcessor.java index 44ad0e74dd..7c913bebcf 100644 --- a/src/client/processor/MakerProcessor.java +++ b/src/client/processor/MakerProcessor.java @@ -20,6 +20,7 @@ package client.processor; import client.MapleClient; +import client.MapleCharacter; import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; @@ -199,7 +200,7 @@ public class MakerProcessor { c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded)); c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false); - if(toCreate == 4260003 && c.getPlayer().getQuestStatus(6033) == 1) { + if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) { c.getAbstractPlayerInteraction().setQuestProgress(6033, 1); } } else { @@ -282,6 +283,10 @@ public class MakerProcessor { return null; } + public static int getMakerSkillLevel(MapleCharacter chr) { + return chr.getSkillLevel((chr.getJob().getId() / 1000) * 10000000 + 1007); + } + private static short getCreateStatus(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) { if(recipe == null) { return -1; @@ -299,7 +304,7 @@ public class MakerProcessor { return 3; } - if(c.getPlayer().getSkillLevel((c.getPlayer().getJob().getId() / 1000) * 10000000 + 1007) < recipe.getReqSkillLevel()) { + if(getMakerSkillLevel(c.getPlayer()) < recipe.getReqSkillLevel()) { return 4; } diff --git a/src/constants/ItemConstants.java b/src/constants/ItemConstants.java index 418506fe33..40f6e9afcc 100644 --- a/src/constants/ItemConstants.java +++ b/src/constants/ItemConstants.java @@ -45,6 +45,7 @@ public final class ItemConstants { public final static int SANDBOX = 0x40; // let 0x40 until it's proven something uses this public final static int PET_COME = 0x80; public final static int ACCOUNT_SHARING = 0x100; + public final static int MERGE_UNTRADEABLE = 0x200; public final static boolean EXPIRING_ITEMS = true; public final static Set permanentItemids = new HashSet<>(); diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index 857331d265..2f3c64c057 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -82,7 +82,7 @@ public class ServerConstants { public static final boolean USE_ENFORCE_HPMP_SWAP = false; //Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats. public static final boolean USE_ENFORCE_MOB_LEVEL_RANGE = true; //Players N levels below the killed mob will gain no experience from defeating it. public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs. - public static final boolean USE_ENFORCE_JOB_SP_RANGE = true; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved. + public static final boolean USE_ENFORCE_JOB_SP_RANGE = false; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved. public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players. public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants. public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues) @@ -115,6 +115,10 @@ public class ServerConstants { public static final boolean USE_MAKER_PERMISSIVE_ATKUP = true; //Allows players to use attack-based strengthening gems on non-weapon items. public static final boolean USE_MAKER_FEE_HEURISTICS = true; //Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than requested by the client-side). + //Custom Configuration + public static final boolean USE_ENABLE_CUSTOM_NPC_SCRIPT = true;//Enables usage of custom HeavenMS NPC scripts (Agent E, Coco, etc). Will not disable Abdula (it's actually useful for the gameplay), quests or NPC shops. + public static final boolean USE_STARTER_MERGE = false; //Allows any players to use the Equipment Merge custom mechanic (as opposed to the high-level, Maker lv3 requisites). + //Commands Configuration public static final boolean BLOCK_GENERATE_CASH_ITEM = false; //Prevents creation of cash items with the item/drop command. public static final boolean USE_WHOLE_SERVER_RANKING = false; //Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of separated by worlds. @@ -133,7 +137,7 @@ public class ServerConstants { public static final int PARTY_EXPERIENCE_MOD = 1; //Change for event stuff. //Miscellaneous Configuration - public static String TIMEZONE = "-GMT3"; + public static String TIMEZONE = "GMT-3"; public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values). public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions. public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 20; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled. @@ -178,6 +182,7 @@ public class ServerConstants { public static final boolean USE_ULTRA_THREE_SNAILS = true; //Massive damage on shell toss. //Other Skills Configuration + public static final boolean USE_FULL_ARAN_SKILLSET = false; //Enables starter availability to all Aran job skills. Suggestion thanks to Masterrulax. public static final boolean USE_FAST_REUSE_HERO_WILL = true;//Greatly reduce cooldown on Hero's Will. public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs. Suggestion thanks to Celestial. public static final boolean USE_UNDISPEL_HOLY_SHIELD = true;//Holy shield buff also prevents players from suffering dispel from mobs. @@ -245,6 +250,7 @@ public class ServerConstants { public static final long EVENT_LOBBY_DELAY = 10; //Cooldown duration in seconds before reopening an event lobby. //Dojo Configuration + public static final boolean USE_FAST_DOJO_UPGRADE = true; //Reduced Dojo training points amount required for a belt upgrade. public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo? public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack diff --git a/src/constants/skills/WindArcher.java b/src/constants/skills/WindArcher.java index cb3039d9f2..c692af89e8 100644 --- a/src/constants/skills/WindArcher.java +++ b/src/constants/skills/WindArcher.java @@ -33,6 +33,7 @@ public class WindArcher { public static final int BOW_BOOSTER = 13101001; public static final int FINAL_ATTACK = 13101002; public static final int SOUL_ARROW = 13101003; + public static final int STORM_BREAK = 13101005; public static final int WIND_WALK = 13101006; public static final int HURRICANE = 13111002; public static final int PUPPET = 13111004; diff --git a/src/net/PacketProcessor.java b/src/net/PacketProcessor.java index e43cebd324..01fb6993d0 100644 --- a/src/net/PacketProcessor.java +++ b/src/net/PacketProcessor.java @@ -136,6 +136,7 @@ public final class PacketProcessor { registerHandler(RecvOpcode.NPC_TALK, new NPCTalkHandler()); registerHandler(RecvOpcode.NPC_TALK_MORE, new NPCMoreTalkHandler()); registerHandler(RecvOpcode.QUEST_ACTION, new QuestActionHandler()); + registerHandler(RecvOpcode.GRENADE_EFFECT, new GrenadeEffectHandler()); registerHandler(RecvOpcode.NPC_SHOP, new NPCShopHandler()); registerHandler(RecvOpcode.ITEM_SORT, new InventoryMergeHandler()); registerHandler(RecvOpcode.ITEM_MOVE, new ItemMoveHandler()); diff --git a/src/net/opcodes/RecvOpcode.java b/src/net/opcodes/RecvOpcode.java index 8b8c6e3e09..6040f9fb03 100644 --- a/src/net/opcodes/RecvOpcode.java +++ b/src/net/opcodes/RecvOpcode.java @@ -114,7 +114,8 @@ public enum RecvOpcode { TROCK_ADD_MAP(0x66), REPORT(0x6A), QUEST_ACTION(0x6B), - //lolno + //USER_CALC_DAMAGE_STAT_SET_REQUEST(0x6C), + GRENADE_EFFECT(0x6D), SKILL_MACRO(0x6E), USE_ITEM_REWARD(0x70), MAKER_SKILL(0x71), diff --git a/src/net/opcodes/SendOpcode.java b/src/net/opcodes/SendOpcode.java index 2786d6f7a4..55110e68c6 100644 --- a/src/net/opcodes/SendOpcode.java +++ b/src/net/opcodes/SendOpcode.java @@ -228,6 +228,7 @@ public enum SendOpcode { GIVE_FOREIGN_BUFF(0xC7), CANCEL_FOREIGN_BUFF(0xC8), UPDATE_PARTYMEMBER_HP(0xC9), + THROW_GRENADE(0xCC), CANCEL_CHAIR(0xCD), SHOW_ITEM_GAIN_INCHAT(0xCE), DOJO_WARP_UP(0xCF), diff --git a/src/net/server/PlayerStorage.java b/src/net/server/PlayerStorage.java index 9bd6d1647d..ba77848f2e 100644 --- a/src/net/server/PlayerStorage.java +++ b/src/net/server/PlayerStorage.java @@ -102,7 +102,7 @@ public class PlayerStorage { for(MapleCharacter mc : chrList) { MapleClient client = mc.getClient(); if(client != null) { - client.disconnect(true, false); + client.forceDisconnect(); } } diff --git a/src/net/server/Server.java b/src/net/server/Server.java index 4742b4cb9c..4f7ee9261b 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -85,8 +85,10 @@ import client.newyear.NewYearCardRecord; import constants.ItemConstants; import constants.GameConstants; import constants.ServerConstants; +import java.util.TimeZone; import server.CashShop.CashItemFactory; import server.MapleSkillbookInformationProvider; +import server.ThreadManager; import server.TimerManager; import server.life.MaplePlayerNPCFactory; import server.quest.MapleQuest; @@ -147,6 +149,10 @@ public class Server { private boolean online = false; public static long uptime = System.currentTimeMillis(); + public int getCurrentTimestamp() { + return (int) (Server.getInstance().getCurrentTime() - Server.uptime); + } + public long getCurrentTime() { // returns a slightly delayed time value, under frequency of UPDATE_INTERVAL return serverCurrentTime; } @@ -825,6 +831,33 @@ public class Server { return rankSystem; } + private static void clearUnreferencedPetIds() { + PreparedStatement ps = null; + Connection con = null; + try { + con = DatabaseConnection.getConnection(); + + ps = con.prepareStatement("UPDATE inventoryitems SET petid = -1, expiration = 0 WHERE petid != -1 AND petid NOT IN (SELECT petid FROM pets)"); + ps.executeUpdate(); + + ps.close(); + con.close(); + } catch(SQLException ex) { + ex.printStackTrace(); + } finally { + try { + if(ps != null && !ps.isClosed()) { + ps.close(); + } + if(con != null && !con.isClosed()) { + con.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + public void init() { Properties p = loadWorldINI(); if(p == null) { @@ -832,11 +865,12 @@ public class Server { } System.out.println("HeavenMS v" + ServerConstants.VERSION + " starting up.\r\n"); - if(ServerConstants.SHUTDOWNHOOK) Runtime.getRuntime().addShutdownHook(new Thread(shutdown(false))); + TimeZone.setDefault(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + Connection c = null; try { c = DatabaseConnection.getConnection(); @@ -856,6 +890,7 @@ public class Server { sqle.printStackTrace(); } + clearUnreferencedPetIds(); MapleCashidGenerator.loadExistentCashIdsFromDb(); IoBuffer.setUseDirectBuffer(false); @@ -863,6 +898,7 @@ public class Server { acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory())); + ThreadManager.getInstance().start(); TimerManager tMan = TimerManager.getInstance(); tMan.start(); tMan.register(tMan.purge(), ServerConstants.PURGING_INTERVAL);//Purging ftw... @@ -1716,6 +1752,7 @@ public class Server { resetServerWorlds(); + ThreadManager.getInstance().stop(); TimerManager.getInstance().purge(); TimerManager.getInstance().stop(); diff --git a/src/net/server/audit/ThreadTracker.java b/src/net/server/audit/ThreadTracker.java index f70f834de1..7ad940f346 100644 --- a/src/net/server/audit/ThreadTracker.java +++ b/src/net/server/audit/ThreadTracker.java @@ -153,7 +153,7 @@ public class ThreadTracker { StackTraceElement[] ste = threads.get(l).getStackTrace(); if(ste.length > 0) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); String df = dateFormat.format(new Date()); FilePrinter.print(FilePrinter.DEADLOCK_LOCKS, printThreadLog(tt, df)); @@ -195,7 +195,7 @@ public class ThreadTracker { } } else { // print status DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); FilePrinter.printError(FilePrinter.DEADLOCK_STATE, printThreadTrackerState(dateFormat.format(new Date()))); //FilePrinter.printError(FilePrinter.DEADLOCK_STATE, "[" + dateFormat.format(new Date()) + "] Presenting current lock path for lockid " + lockId.name() + ".\r\n" + printLockStatus(lockId) + "\r\n-------------------------------\r\n"); diff --git a/src/net/server/audit/locks/active/TrackerReadLock.java b/src/net/server/audit/locks/active/TrackerReadLock.java index 7fac952520..22fbc26183 100644 --- a/src/net/server/audit/locks/active/TrackerReadLock.java +++ b/src/net/server/audit/locks/active/TrackerReadLock.java @@ -62,7 +62,7 @@ public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements if(ServerConstants.USE_THREAD_TRACKER) { if(deadlockedState != null) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); diff --git a/src/net/server/audit/locks/active/TrackerReentrantLock.java b/src/net/server/audit/locks/active/TrackerReentrantLock.java index a96862896d..c60fe5f892 100644 --- a/src/net/server/audit/locks/active/TrackerReentrantLock.java +++ b/src/net/server/audit/locks/active/TrackerReentrantLock.java @@ -64,7 +64,7 @@ public class TrackerReentrantLock extends ReentrantLock implements MonitoredReen if(ServerConstants.USE_THREAD_TRACKER) { if(deadlockedState != null) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); diff --git a/src/net/server/audit/locks/active/TrackerWriteLock.java b/src/net/server/audit/locks/active/TrackerWriteLock.java index 41c9e92745..82bc429106 100644 --- a/src/net/server/audit/locks/active/TrackerWriteLock.java +++ b/src/net/server/audit/locks/active/TrackerWriteLock.java @@ -60,7 +60,7 @@ public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implement if(ServerConstants.USE_THREAD_TRACKER) { if(deadlockedState != null) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); diff --git a/src/net/server/audit/locks/empty/EmptyReadLock.java b/src/net/server/audit/locks/empty/EmptyReadLock.java index e5e8aa3c73..9e98b776bf 100644 --- a/src/net/server/audit/locks/empty/EmptyReadLock.java +++ b/src/net/server/audit/locks/empty/EmptyReadLock.java @@ -41,7 +41,7 @@ public class EmptyReadLock implements MonitoredReadLock { private static String printThreadStack(StackTraceElement[] list) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); String df = dateFormat.format(new Date()); String s = "\r\n" + df + "\r\n"; diff --git a/src/net/server/audit/locks/empty/EmptyReentrantLock.java b/src/net/server/audit/locks/empty/EmptyReentrantLock.java index 1753efcb43..dd2916c1e9 100644 --- a/src/net/server/audit/locks/empty/EmptyReentrantLock.java +++ b/src/net/server/audit/locks/empty/EmptyReentrantLock.java @@ -41,7 +41,7 @@ public class EmptyReentrantLock implements MonitoredReentrantLock { private static String printThreadStack(StackTraceElement[] list) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); String df = dateFormat.format(new Date()); String s = "\r\n" + df + "\r\n"; diff --git a/src/net/server/audit/locks/empty/EmptyWriteLock.java b/src/net/server/audit/locks/empty/EmptyWriteLock.java index f051931c5c..c1615f2fa3 100644 --- a/src/net/server/audit/locks/empty/EmptyWriteLock.java +++ b/src/net/server/audit/locks/empty/EmptyWriteLock.java @@ -41,7 +41,7 @@ public class EmptyWriteLock implements MonitoredWriteLock { private static String printThreadStack(StackTraceElement[] list) { DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + dateFormat.setTimeZone(TimeZone.getDefault()); String df = dateFormat.format(new Date()); String s = "\r\n" + df + "\r\n"; diff --git a/src/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/net/server/channel/handlers/AbstractDealDamageHandler.java index 9d945b0af6..df5168eaeb 100644 --- a/src/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -259,8 +259,9 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl else if(attack.skill == Shadower.BOOMERANG_STEP) distanceToDetect += 60000; - if(distance > distanceToDetect) { + if (distance > distanceToDetect) { AutobanFactory.DISTANCE_HACK.alert(player, "Distance Sq to monster: " + distance + " SID: " + attack.skill + " MID: " + monster.getId()); + monster.refreshMobPosition(); } int totDamageToOneMonster = 0; @@ -338,7 +339,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } } - if (job == 2111 || job == 2112) { + if (player.isAran()) { if (player.getBuffedValue(MapleBuffStat.WK_CHARGE) != null) { Skill snowCharge = SkillFactory.getSkill(Aran.SNOW_CHARGE); if (totDamageToOneMonster > 0) { diff --git a/src/net/server/channel/handlers/AutoAggroHandler.java b/src/net/server/channel/handlers/AutoAggroHandler.java index e611bbb72a..622f7ef75c 100644 --- a/src/net/server/channel/handlers/AutoAggroHandler.java +++ b/src/net/server/channel/handlers/AutoAggroHandler.java @@ -26,6 +26,7 @@ import net.AbstractMaplePacketHandler; import server.life.MapleMonster; import server.maps.MapleMap; import tools.data.input.SeekableLittleEndianAccessor; +import client.MapleCharacter; public final class AutoAggroHandler extends AbstractMaplePacketHandler { @@ -38,18 +39,26 @@ public final class AutoAggroHandler extends AbstractMaplePacketHandler { int oid = slea.readInt(); MapleMonster monster = map.getMonsterByOid(oid); - if (monster != null && monster.getController() != null) { - if (!monster.isControllerHasAggro()) { - if (map.getCharacterById(monster.getController().getId()) == null) { - monster.switchController(c.getPlayer(), true); + if (monster != null) { + MapleCharacter currentController = monster.getController(); + monster.lockMonster(); + try { + if (currentController != null) { + if (!monster.isControllerHasAggro()) { + if (map.getCharacterById(currentController.getId()) == null) { + monster.switchController(c.getPlayer(), true); + } else { + monster.switchController(currentController, true); + } + } else if (map.getCharacterById(currentController.getId()) == null) { + monster.switchController(c.getPlayer(), true); + } } else { - monster.switchController(monster.getController(), true); + monster.switchController(c.getPlayer(), true); } - } else if (map.getCharacterById(monster.getController().getId()) == null) { - monster.switchController(c.getPlayer(), true); + } finally { + monster.unlockMonster(); } - } else if (monster != null && monster.getController() == null) { - monster.switchController(c.getPlayer(), true); } } } diff --git a/src/net/server/channel/handlers/ChangeChannelHandler.java b/src/net/server/channel/handlers/ChangeChannelHandler.java index 0f343fe5d1..c02bb17b59 100644 --- a/src/net/server/channel/handlers/ChangeChannelHandler.java +++ b/src/net/server/channel/handlers/ChangeChannelHandler.java @@ -25,6 +25,7 @@ import net.AbstractMaplePacketHandler; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleClient; import client.autoban.AutobanFactory; +import net.server.Server; /** * @@ -35,7 +36,8 @@ public final class ChangeChannelHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { int channel = slea.readByte() + 1; - c.getPlayer().getAutobanManager().setTimestamp(6, slea.readInt(), 2); + slea.readInt(); + c.getPlayer().getAutobanManager().setTimestamp(6, Server.getInstance().getCurrentTimestamp(), 3); if(c.getChannel() == channel) { AutobanFactory.GENERAL.alert(c.getPlayer(), "CCing to same channel."); c.disconnect(false, false); diff --git a/src/net/server/channel/handlers/GrenadeEffectHandler.java b/src/net/server/channel/handlers/GrenadeEffectHandler.java new file mode 100644 index 0000000000..b08adf7649 --- /dev/null +++ b/src/net/server/channel/handlers/GrenadeEffectHandler.java @@ -0,0 +1,57 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.channel.handlers; + +import client.MapleClient; +import client.MapleCharacter; +import constants.skills.Gunslinger; +import constants.skills.NightWalker; +import net.AbstractMaplePacketHandler; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; +import java.awt.Point; +import tools.FilePrinter; + +/* + * @author GabrielSin + */ +public class GrenadeEffectHandler extends AbstractMaplePacketHandler { + + @Override + public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + MapleCharacter chr = c.getPlayer(); + Point position = new Point(slea.readInt(), slea.readInt()); + int keyDown = slea.readInt(); + int skillId = slea.readInt(); + + switch (skillId) { + case NightWalker.POISON_BOMB: + case Gunslinger.GRENADE: + int skillLevel = chr.getSkillLevel(skillId); + if (skillLevel > 0) { + chr.getMap().broadcastMessage(chr, MaplePacketCreator.throwGrenade(chr.getId(), position, keyDown, skillId, skillLevel), position); + } + break; + default: + FilePrinter.printError(FilePrinter.UNHANDLED_EVENT, "The skill id: " + skillId + " is not coded in " + this.getClass().getName() + "."); + } + } + +} \ No newline at end of file diff --git a/src/net/server/channel/handlers/HealOvertimeHandler.java b/src/net/server/channel/handlers/HealOvertimeHandler.java index 726106708c..44204ddf72 100644 --- a/src/net/server/channel/handlers/HealOvertimeHandler.java +++ b/src/net/server/channel/handlers/HealOvertimeHandler.java @@ -38,12 +38,12 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler { if(!chr.isLoggedinWorld()) return; AutobanManager abm = chr.getAutobanManager(); - int timestamp = (int) (Server.getInstance().getCurrentTime() - Server.uptime); + int timestamp = Server.getInstance().getCurrentTimestamp(); slea.skip(8); short healHP = slea.readShort(); if (healHP != 0) { - abm.setTimestamp(8, timestamp, 3); // thanks Vcoc for pointing out d/c happening here + abm.setTimestamp(8, timestamp, 4); // thanks Vcoc for pointing out d/c happening here if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing"); int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa... @@ -58,7 +58,7 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler { } short healMP = slea.readShort(); if (healMP != 0 && healMP < 1000) { - abm.setTimestamp(9, timestamp, 3); + abm.setTimestamp(9, timestamp, 4); if ((abm.getLastSpam(1) + 1500) > timestamp) AutobanFactory.FAST_MP_HEALING.addPoint(abm, "Fast mp healing"); chr.addMP(healMP); abm.spam(1, timestamp); diff --git a/src/net/server/channel/handlers/InventoryMergeHandler.java b/src/net/server/channel/handlers/InventoryMergeHandler.java index cbc2f21621..43ec9f437e 100644 --- a/src/net/server/channel/handlers/InventoryMergeHandler.java +++ b/src/net/server/channel/handlers/InventoryMergeHandler.java @@ -31,6 +31,7 @@ import client.MapleClient; import client.inventory.Item; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; +import net.server.Server; import server.MapleItemInformationProvider; public final class InventoryMergeHandler extends AbstractMaplePacketHandler { @@ -38,7 +39,8 @@ public final class InventoryMergeHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - chr.getAutobanManager().setTimestamp(2, slea.readInt(), 3); + slea.readInt(); + chr.getAutobanManager().setTimestamp(2, Server.getInstance().getCurrentTimestamp(), 4); if(!ServerConstants.USE_ITEM_SORT) { c.announce(MaplePacketCreator.enableActions()); diff --git a/src/net/server/channel/handlers/InventorySortHandler.java b/src/net/server/channel/handlers/InventorySortHandler.java index 26e088a5d6..8d6e30505d 100644 --- a/src/net/server/channel/handlers/InventorySortHandler.java +++ b/src/net/server/channel/handlers/InventorySortHandler.java @@ -24,7 +24,6 @@ package net.server.channel.handlers; import java.util.ArrayList; import java.util.List; -import constants.ServerConstants; import net.AbstractMaplePacketHandler; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -35,7 +34,9 @@ import client.inventory.Equip; import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.ModifyInventory; +import constants.ServerConstants; import server.MapleItemInformationProvider; +import net.server.Server; /** * @@ -187,7 +188,8 @@ public final class InventorySortHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - chr.getAutobanManager().setTimestamp(3, slea.readInt(), 3); + slea.readInt(); + chr.getAutobanManager().setTimestamp(3, Server.getInstance().getCurrentTimestamp(), 4); if(!ServerConstants.USE_ITEM_SORT) { c.announce(MaplePacketCreator.enableActions()); diff --git a/src/net/server/channel/handlers/MoveLifeHandler.java b/src/net/server/channel/handlers/MoveLifeHandler.java index 6ff0918301..1bc1acb6bb 100644 --- a/src/net/server/channel/handlers/MoveLifeHandler.java +++ b/src/net/server/channel/handlers/MoveLifeHandler.java @@ -61,7 +61,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { } MapleMonster monster = (MapleMonster) mmo; - List banishPlayers = new LinkedList<>(); + List banishPlayers = null; byte pNibbles = slea.readByte(); byte rawActivity = slea.readByte(); @@ -70,7 +70,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { short pOption = slea.readShort(); slea.skip(8); - if (rawActivity >= 0) { + if (rawActivity >= 0) { rawActivity = (byte) (rawActivity & 0xFF >> 1); } @@ -78,7 +78,9 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { boolean isSkill = inRangeInclusive(rawActivity, 21, 25); byte attackId = (byte) (isAttack ? rawActivity - 12 : -1); - boolean nextMovementCouldBeSkill = (pNibbles & 0x0F) != 0; + isSkill |= (pNibbles != 0); + + boolean nextMovementCouldBeSkill = true; boolean pUnk = (pNibbles & 0xF0) != 0; boolean currentController = (monster.getController() == player); @@ -86,6 +88,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { MobSkill toUse = null; int useSkillId = 0, useSkillLevel = 0; + int rnd = 0; if (isSkill) { int noSkills = monster.getNoSkills(); if (noSkills > 0) { @@ -95,21 +98,21 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { useSkillId = skillToUse.getLeft(); useSkillLevel = skillToUse.getRight(); toUse = MobSkillFactory.getMobSkill(useSkillId, useSkillLevel); - } else { + + rnd = rndSkill; nextMovementCouldBeSkill = false; } - } else { - nextMovementCouldBeSkill = false; } int mobMp; if (toUse != null && toUse.getHP() >= (int) (((float) monster.getHp() / monster.getMaxHp()) * 100) && monster.canUseSkill(toUse)) { mobMp = monster.getMp(); - + int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(toUse); - if(animationTime > 0) { - toUse.applyDelayedEffect(player, monster, true, banishPlayers, animationTime); + if(animationTime > 0 && toUse.getSkillId() != 129) { + toUse.applyDelayedEffect(player, monster, true, animationTime); } else { + banishPlayers = new LinkedList<>(); toUse.applyEffect(player, monster, true, banishPlayers); } } else { @@ -138,36 +141,45 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { short start_y = slea.readShort(); // hmm... Point startPos = new Point(start_x, start_y - 2); List res = parseMovement(slea); - if (!currentController) { - if (monster.isAttackedBy(player)) { - monster.switchController(player, true); - } else { - return; - } - } else if (rawActivity == -1 && monster.isControllerKnowsAboutAggro() && !monster.isMobile() && !monster.isFirstAttack()) { - monster.setControllerHasAggro(false); - monster.setControllerKnowsAboutAggro(false); - } + + boolean aggro; + monster.lockMonster(); + try { + if (!currentController) { + if (monster.isAttackedBy(player)) { + monster.switchController(player, true); + } else { + return; + } + } else if (rawActivity == -1 && monster.isControllerKnowsAboutAggro() && !monster.isMobile() && !monster.isFirstAttack()) { + monster.setControllerHasAggro(false); + monster.setControllerKnowsAboutAggro(false); + } + + aggro = monster.isControllerHasAggro(); + if (aggro) { + monster.setControllerKnowsAboutAggro(true); + } + } finally { + monster.unlockMonster(); + } - boolean aggro = monster.isControllerHasAggro(); if (toUse != null) { c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, toUse.getSkillId(), toUse.getSkillLevel())); } else { c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro)); } - if (aggro) { - monster.setControllerKnowsAboutAggro(true); - } - if (res != null) { map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition()); updatePosition(res, monster, -2); map.moveMonster(monster, monster.getPosition()); } - for (MapleCharacter chr : banishPlayers) { - chr.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg()); + if (banishPlayers != null) { + for (MapleCharacter chr : banishPlayers) { + chr.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg()); + } } } diff --git a/src/net/server/channel/handlers/PetCommandHandler.java b/src/net/server/channel/handlers/PetCommandHandler.java index 997ec27e74..cc75179265 100644 --- a/src/net/server/channel/handlers/PetCommandHandler.java +++ b/src/net/server/channel/handlers/PetCommandHandler.java @@ -54,8 +54,9 @@ public final class PetCommandHandler extends AbstractMaplePacketHandler { if (Randomizer.nextInt(101) <= petCommand.getProbability()) { pet.gainClosenessFullness(chr, petCommand.getIncrease(), 0, command); + chr.getMap().broadcastMessage(MaplePacketCreator.commandResponse(chr.getId(), petIndex, false, command, false)); } else { - chr.getMap().broadcastMessage(MaplePacketCreator.commandResponse(chr.getId(), petIndex, command, false)); + chr.getMap().broadcastMessage(MaplePacketCreator.commandResponse(chr.getId(), petIndex, true, command, false)); } } } diff --git a/src/net/server/channel/handlers/PetFoodHandler.java b/src/net/server/channel/handlers/PetFoodHandler.java index 5a1e997e3c..23e9b7f4b0 100644 --- a/src/net/server/channel/handlers/PetFoodHandler.java +++ b/src/net/server/channel/handlers/PetFoodHandler.java @@ -21,6 +21,7 @@ */ package net.server.channel.handlers; +import net.AbstractMaplePacketHandler; import client.MapleCharacter; import client.MapleClient; import client.inventory.MapleInventory; @@ -28,8 +29,8 @@ import client.inventory.MapleInventoryType; import client.inventory.MaplePet; import client.autoban.AutobanManager; import client.inventory.Item; -import net.AbstractMaplePacketHandler; import client.inventory.manipulator.MapleInventoryManipulator; +import net.server.Server; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -44,7 +45,8 @@ public final class PetFoodHandler extends AbstractMaplePacketHandler { return; } abm.spam(2); - abm.setTimestamp(1, slea.readInt(), 3); + slea.readInt(); // timestamp issue detected thanks to Masterrulax + abm.setTimestamp(1, Server.getInstance().getCurrentTimestamp(), 3); if (chr.getNoPets() == 0) { c.announce(MaplePacketCreator.enableActions()); return; diff --git a/src/net/server/channel/handlers/RangedAttackHandler.java b/src/net/server/channel/handlers/RangedAttackHandler.java index 7d89da5ded..b432d75e1e 100644 --- a/src/net/server/channel/handlers/RangedAttackHandler.java +++ b/src/net/server/channel/handlers/RangedAttackHandler.java @@ -163,7 +163,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } } } - } + } boolean soulArrow = chr.getBuffedValue(MapleBuffStat.SOULARROW) != null; boolean shadowClaw = chr.getBuffedValue(MapleBuffStat.SHADOW_CLAW) != null; if (projectile != 0) { @@ -179,7 +179,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } } - if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006 || attack.skill == 4111004) { + if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006 || attack.skill == 4111004 || attack.skill == 13101005) { int visProjectile = projectile; //visible projectile sent to players if (ItemConstants.isThrowingStar(projectile)) { MapleInventory cash = chr.getInventory(MapleInventoryType.CASH); @@ -192,7 +192,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { } } } - } else if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) { + } else if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006 || attack.skill == 13101005) { visProjectile = 0; } diff --git a/src/net/server/channel/handlers/ScriptedItemHandler.java b/src/net/server/channel/handlers/ScriptedItemHandler.java index 10e4df9563..98a7511ed1 100644 --- a/src/net/server/channel/handlers/ScriptedItemHandler.java +++ b/src/net/server/channel/handlers/ScriptedItemHandler.java @@ -49,7 +49,7 @@ public final class ScriptedItemHandler extends AbstractMaplePacketHandler { if (item == null || item.getItemId() != itemId || item.getQuantity() < 1 || !ism.scriptExists(info.getScript())) { return; } - ism.getItemScript(c, info.getScript()); + ism.runItemScript(c, info.getScript()); c.announce(MaplePacketCreator.enableActions()); //NPCScriptManager.getInstance().start(c, info.getNpc(), null, null); } diff --git a/src/net/server/channel/handlers/SpecialMoveHandler.java b/src/net/server/channel/handlers/SpecialMoveHandler.java index 03cf2cbaf1..6b72dbcd76 100644 --- a/src/net/server/channel/handlers/SpecialMoveHandler.java +++ b/src/net/server/channel/handlers/SpecialMoveHandler.java @@ -30,7 +30,6 @@ import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleCharacter; import client.MapleClient; -import client.MapleStat; import client.Skill; import client.SkillFactory; import constants.ServerConstants; @@ -41,14 +40,15 @@ import constants.skills.Hero; import constants.skills.Paladin; import constants.skills.Priest; import constants.skills.SuperGM; - +import net.server.Server; public final class SpecialMoveHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); - chr.getAutobanManager().setTimestamp(4, slea.readInt(), 3); + slea.readInt(); + chr.getAutobanManager().setTimestamp(4, Server.getInstance().getCurrentTimestamp(), 5); int skillid = slea.readInt(); /* @@ -99,9 +99,14 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler { chr.getMap().broadcastMessage(chr, MaplePacketCreator.showMagnet(mobId, success), false); MapleMonster monster = chr.getMap().getMonsterByOid(mobId); if (monster != null) { - if (!monster.isBoss()) { - monster.switchController(chr, monster.isControllerHasAggro()); - } + if (!monster.isBoss()) { + monster.lockMonster(); + try { + monster.switchController(chr, monster.isControllerHasAggro()); + } finally { + monster.unlockMonster(); + } + } } } byte direction = slea.readByte(); diff --git a/src/net/server/channel/handlers/TouchReactorHandler.java b/src/net/server/channel/handlers/TouchReactorHandler.java index 5091355fc3..a5685f34a3 100644 --- a/src/net/server/channel/handlers/TouchReactorHandler.java +++ b/src/net/server/channel/handlers/TouchReactorHandler.java @@ -32,6 +32,8 @@ import tools.data.input.SeekableLittleEndianAccessor; * @author Generic */ public final class TouchReactorHandler extends AbstractMaplePacketHandler { + + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { int oid = slea.readInt(); MapleReactor reactor = c.getPlayer().getMap().getReactorByOid(oid); diff --git a/src/net/server/channel/handlers/UseCatchItemHandler.java b/src/net/server/channel/handlers/UseCatchItemHandler.java index c7782d38b7..753fefbe68 100644 --- a/src/net/server/channel/handlers/UseCatchItemHandler.java +++ b/src/net/server/channel/handlers/UseCatchItemHandler.java @@ -27,6 +27,7 @@ import client.inventory.MapleInventoryType; import client.autoban.AutobanManager; import constants.ItemConstants; import net.AbstractMaplePacketHandler; +import net.server.Server; import client.inventory.manipulator.MapleInventoryManipulator; import server.life.MapleMonster; import tools.MaplePacketCreator; @@ -41,7 +42,8 @@ public final class UseCatchItemHandler extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); AutobanManager abm = chr.getAutobanManager(); - abm.setTimestamp(5, slea.readInt(), 3); + slea.readInt(); + abm.setTimestamp(5, Server.getInstance().getCurrentTimestamp(), 4); slea.readShort(); int itemId = slea.readInt(); int monsterid = slea.readInt(); diff --git a/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java b/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java index 91a0b733a1..ac5df67148 100644 --- a/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java +++ b/src/net/server/channel/handlers/UseOwlOfMinervaHandler.java @@ -35,7 +35,6 @@ import constants.GameConstants; /** * @author Ronan */ - public final class UseOwlOfMinervaHandler extends AbstractMaplePacketHandler { @Override diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index f4228208fa..13017a5a17 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -1402,7 +1402,9 @@ public class World { int dpVal = dp.getValue() + 1; if(dpVal == ServerConstants.MOUNT_EXHAUST_COUNT) { - chr.runTirednessSchedule(); + if (!chr.runTirednessSchedule()) { + continue; + } dpVal = 0; } diff --git a/src/scripting/AbstractPlayerInteraction.java b/src/scripting/AbstractPlayerInteraction.java index 5d76522c9e..fedd040262 100644 --- a/src/scripting/AbstractPlayerInteraction.java +++ b/src/scripting/AbstractPlayerInteraction.java @@ -512,21 +512,21 @@ public class AbstractPlayerInteraction { petId = MaplePet.createPet(id); if(from != null) { - evolved = MaplePet.loadFromDb(id, (short) 0, petId); + evolved = MaplePet.loadFromDb(id, (short) 0, petId); - Point pos = getPlayer().getPosition(); - pos.y -= 12; - evolved.setPos(pos); - evolved.setFh(getPlayer().getMap().getFootholds().findBelow(evolved.getPos()).getId()); - evolved.setStance(0); - evolved.setSummoned(true); + Point pos = getPlayer().getPosition(); + pos.y -= 12; + evolved.setPos(pos); + evolved.setFh(getPlayer().getMap().getFootholds().findBelow(evolved.getPos()).getId()); + evolved.setStance(0); + evolved.setSummoned(true); - evolved.setName(from.getName().compareTo(MapleItemInformationProvider.getInstance().getName(from.getItemId())) != 0 ? from.getName() : MapleItemInformationProvider.getInstance().getName(id)); - evolved.setCloseness(from.getCloseness()); - evolved.setFullness(from.getFullness()); - evolved.setLevel(from.getLevel()); - evolved.setExpiration(System.currentTimeMillis() + expires); - evolved.saveToDb(); + evolved.setName(from.getName().compareTo(MapleItemInformationProvider.getInstance().getName(from.getItemId())) != 0 ? from.getName() : MapleItemInformationProvider.getInstance().getName(id)); + evolved.setCloseness(from.getCloseness()); + evolved.setFullness(from.getFullness()); + evolved.setLevel(from.getLevel()); + evolved.setExpiration(System.currentTimeMillis() + expires); + evolved.saveToDb(); } //MapleInventoryManipulator.addById(c, id, (short) 1, null, petId, expires == -1 ? -1 : System.currentTimeMillis() + expires); diff --git a/src/scripting/event/EventInstanceManager.java b/src/scripting/event/EventInstanceManager.java index fd912b9658..e04da80608 100644 --- a/src/scripting/event/EventInstanceManager.java +++ b/src/scripting/event/EventInstanceManager.java @@ -69,6 +69,7 @@ import net.server.coordinator.MapleEventRecallCoordinator; import scripting.AbstractPlayerInteraction; import scripting.event.worker.EventScriptScheduler; import server.MapleItemInformationProvider; +import server.ThreadManager; import server.life.MapleLifeFactory; import server.life.MapleNPC; import tools.MaplePacketCreator; @@ -228,51 +229,42 @@ public class EventInstanceManager { } - public synchronized void registerPlayer(MapleCharacter chr) { + public synchronized void registerPlayer(final MapleCharacter chr) { if (chr == null || !chr.isLoggedinWorld() || disposed) { return; } + wL.lock(); try { - wL.lock(); - try { - if(chars.containsKey(chr.getId())) { - return; - } - - chars.put(chr.getId(), chr); - chr.setEventInstance(this); - } finally { - wL.unlock(); + if(chars.containsKey(chr.getId())) { + return; } - - sL.lock(); - try { - em.getIv().invokeFunction("playerEntry", this, chr); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + + chars.put(chr.getId(), chr); + chr.setEventInstance(this); + } finally { + wL.unlock(); + } + + try { + em.getIv().invokeFunction("playerEntry", EventInstanceManager.this, chr); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } } - public void exitPlayer(MapleCharacter chr) { + public void exitPlayer(final MapleCharacter chr) { if (chr == null || !chr.isLoggedin()){ return; } - try { - unregisterPlayer(chr); - - sL.lock(); - try { - em.getIv().invokeFunction("playerExit", this, chr); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + + unregisterPlayer(chr); + + try { + em.getIv().invokeFunction("playerExit", EventInstanceManager.this, chr); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } } public void dropMessage(int type, String message) { @@ -297,15 +289,10 @@ public class EventInstanceManager { event_schedule = TimerManager.getInstance().schedule(new Runnable() { @Override public void run() { + dismissEventTimer(); + try { - dismissEventTimer(); - - sL.lock(); - try { - em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this); - } finally { - sL.unlock(); - } + em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this); } catch (ScriptException | NoSuchMethodException ex) { Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement scheduledTimeout function.", ex); } @@ -314,31 +301,25 @@ public class EventInstanceManager { } public void addEventTimer(long time) { - if(event_schedule != null) { - if(event_schedule.cancel(false)) { + if (event_schedule != null) { + if (event_schedule.cancel(false)) { long nextTime = getTimeLeft() + time; eventTime += time; event_schedule = TimerManager.getInstance().schedule(new Runnable() { @Override public void run() { + dismissEventTimer(); + try { - dismissEventTimer(); - - sL.lock(); - try { - em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this); - } finally { - sL.unlock(); - } + em.getIv().invokeFunction("scheduledTimeout", EventInstanceManager.this); } catch (ScriptException | NoSuchMethodException ex) { Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement scheduledTimeout function.", ex); } } }, nextTime); } - } - else { + } else { startEventTimer(time); } } @@ -358,6 +339,7 @@ public class EventInstanceManager { event_schedule.cancel(false); event_schedule = null; } + dismissEventTimer(); } @@ -370,7 +352,7 @@ public class EventInstanceManager { } public void registerParty(MapleCharacter chr) { - if(chr.isPartyLeader()) { + if (chr.isPartyLeader()) { registerParty(chr.getParty(), chr.getMap()); } } @@ -390,20 +372,16 @@ public class EventInstanceManager { private void registerExpeditionTeam(MapleExpedition exped, int recruitMap) { expedition = exped; - for(MapleCharacter chr: exped.getMembers()) { - if(chr.getMapId() == recruitMap) + for (MapleCharacter chr: exped.getMembers()) { + if (chr.getMapId() == recruitMap) { registerPlayer(chr); + } } } - public void unregisterPlayer(MapleCharacter chr) { + public void unregisterPlayer(final MapleCharacter chr) { try { - sL.lock(); - try { - em.getIv().invokeFunction("playerUnregistered", EventInstanceManager.this, chr); - } finally { - sL.unlock(); - } + em.getIv().invokeFunction("playerUnregistered", EventInstanceManager.this, chr); } catch (ScriptException | NoSuchMethodException ex) { Logger.getLogger(EventManager.class.getName()).log(Level.SEVERE, "Event '" + em.getName() + "' does not implement playerUnregistered function.", ex); } @@ -462,180 +440,140 @@ public class EventInstanceManager { } } - public void movePlayer(MapleCharacter chr) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("moveMap", this, chr); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } - } - - public void changedMap(MapleCharacter chr, int mapId) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("changedMap", this, chr, mapId); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) {} // optional - } - - public void afterChangedMap(MapleCharacter chr, int mapId) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("afterChangedMap", this, chr, mapId); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) {} // optional - } - - public void changedLeader(MapleCharacter ldr) { + public void movePlayer(final MapleCharacter chr) { try { - sL.lock(); - try { - em.getIv().invokeFunction("changedLeader", this, ldr); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } - + em.getIv().invokeFunction("moveMap", EventInstanceManager.this, chr); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } + } + + public void changedMap(final MapleCharacter chr, final int mapId) { + try { + em.getIv().invokeFunction("changedMap", EventInstanceManager.this, chr, mapId); + } catch (ScriptException | NoSuchMethodException ex) {} // optional + } + + public void afterChangedMap(final MapleCharacter chr, final int mapId) { + try { + em.getIv().invokeFunction("afterChangedMap", EventInstanceManager.this, chr, mapId); + } catch (ScriptException | NoSuchMethodException ex) {} // optional + } + + public synchronized void changedLeader(final MapleCharacter ldr) { + try { + em.getIv().invokeFunction("changedLeader", EventInstanceManager.this, ldr); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } + leaderId = ldr.getId(); } - public void monsterKilled(MapleMonster mob, boolean hasKiller) { + public void monsterKilled(final MapleMonster mob, final boolean hasKiller) { + int scriptResult = 0; + sL.lock(); try { mobs.remove(mob); if(eventStarted) { - try { - em.getIv().invokeFunction("monsterKilled", mob, this, hasKiller); - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + scriptResult = 1; if (mobs.isEmpty()) { - try { - em.getIv().invokeFunction("allMonstersDead", this, hasKiller); - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + scriptResult = 2; } } } finally { sL.unlock(); } + + if (scriptResult > 0) { + try { + em.getIv().invokeFunction("monsterKilled", mob, EventInstanceManager.this, hasKiller); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } + + if (scriptResult > 1) { + try { + em.getIv().invokeFunction("allMonstersDead", EventInstanceManager.this, hasKiller); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } + } + } } - public void friendlyKilled(MapleMonster mob, boolean hasKiller) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("friendlyKilled", mob, this, hasKiller); - } finally { - sL.unlock(); - } + public void friendlyKilled(final MapleMonster mob, final boolean hasKiller) { + try { + em.getIv().invokeFunction("friendlyKilled", mob, EventInstanceManager.this, hasKiller); } catch (ScriptException | NoSuchMethodException ex) {} //optional } - public void playerKilled(MapleCharacter chr) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("playerDead", this, chr); - } finally { - sL.unlock(); + public void playerKilled(final MapleCharacter chr) { + ThreadManager.getInstance().newTask(new Runnable() { + @Override + public void run() { + try { + em.getIv().invokeFunction("playerDead", EventInstanceManager.this, chr); + } catch (ScriptException | NoSuchMethodException ex) {} // optional } - } catch (ScriptException | NoSuchMethodException ex) {} // optional + }); } - public void reviveMonster(MapleMonster mob) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("monsterRevive", this, mob); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) {} // optional + public void reviveMonster(final MapleMonster mob) { + try { + em.getIv().invokeFunction("monsterRevive", EventInstanceManager.this, mob); + } catch (ScriptException | NoSuchMethodException ex) {} // optional } - public boolean revivePlayer(MapleCharacter chr) { - try { - Object b; - - sL.lock(); - try { - b = em.getIv().invokeFunction("playerRevive", this, chr); - } finally { - sL.unlock(); + public boolean revivePlayer(final MapleCharacter chr) { + try { + Object b = em.getIv().invokeFunction("playerRevive", EventInstanceManager.this, chr); + if (b instanceof Boolean) { + return (Boolean) b; } - - if (b instanceof Boolean) { - return (Boolean) b; - } - } catch (ScriptException | NoSuchMethodException ex) {} // optional + } catch (ScriptException | NoSuchMethodException ex) {} // optional return true; } - public void playerDisconnected(MapleCharacter chr) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("playerDisconnected", this, chr); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + public void playerDisconnected(final MapleCharacter chr) { + try { + em.getIv().invokeFunction("playerDisconnected", EventInstanceManager.this, chr); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } MapleEventRecallCoordinator.getInstance().storeEventInstance(chr.getId(), this); } - - /** - * - * @param chr - * @param mob - */ - public void monsterKilled(MapleCharacter chr, MapleMonster mob) { - try { - Integer kc = killCount.get(chr); + + public void monsterKilled(MapleCharacter chr, final MapleMonster mob) { + try { int inc; - sL.lock(); - try { - if(ServerConstants.JAVA_8) - inc = (int)em.getIv().invokeFunction("monsterValue", this, mob.getId()); - else - inc = ((Double) em.getIv().invokeFunction("monsterValue", this, mob.getId())).intValue(); - } finally { - sL.unlock(); + if (ServerConstants.JAVA_8) { + inc = (int)em.getIv().invokeFunction("monsterValue", EventInstanceManager.this, mob.getId()); + } else { + inc = ((Double) em.getIv().invokeFunction("monsterValue", EventInstanceManager.this, mob.getId())).intValue(); } - if (kc == null) { - kc = inc; - } else { - kc += inc; - } - killCount.put(chr, kc); - if (expedition != null){ - expedition.monsterKilled(chr, mob); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + if (inc != 0) { + Integer kc = killCount.get(chr); + if (kc == null) { + kc = inc; + } else { + kc += inc; + } + killCount.put(chr, kc); + if (expedition != null){ + expedition.monsterKilled(chr, mob); + } + } + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } } public int getKillCount(MapleCharacter chr) { @@ -650,15 +588,8 @@ public class EventInstanceManager { } finally { rL.unlock(); } - - Thread t = new Thread( new Runnable() { - @Override - public void run() { - dispose(false); - } - }); - t.start(); + dispose(false); } public synchronized void dispose(boolean shutdown) { // should not trigger any event script method after disposed @@ -666,16 +597,11 @@ public class EventInstanceManager { disposed = true; try { - sL.lock(); - try { - em.getIv().invokeFunction("dispose", this); - } finally { - sL.unlock(); - } + em.getIv().invokeFunction("dispose", EventInstanceManager.this); } catch (ScriptException | NoSuchMethodException ex) { ex.printStackTrace(); } - + mapFactory.dispose(); ess.dispose(); @@ -738,13 +664,8 @@ public class EventInstanceManager { @Override public void run() { try { - sL.lock(); - try { - if(em == null) return; - em.getIv().invokeFunction(methodName, EventInstanceManager.this); - } finally { - sL.unlock(); - } + if(em == null) return; + em.getIv().invokeFunction(methodName, EventInstanceManager.this); } catch (ScriptException | NoSuchMethodException ex) { ex.printStackTrace(); } @@ -848,56 +769,36 @@ public class EventInstanceManager { } } - public void leftParty(MapleCharacter chr) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("leftParty", this, chr); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + public void leftParty(final MapleCharacter chr) { + try { + em.getIv().invokeFunction("leftParty", EventInstanceManager.this, chr); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } } public void disbandParty() { try { - sL.lock(); - try { - em.getIv().invokeFunction("disbandParty", this); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + em.getIv().invokeFunction("disbandParty", EventInstanceManager.this); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } } public void clearPQ() { try { - sL.lock(); - try { - em.getIv().invokeFunction("clearPQ", this); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + em.getIv().invokeFunction("clearPQ", EventInstanceManager.this); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } } - public void removePlayer(MapleCharacter chr) { - try { - sL.lock(); - try { - em.getIv().invokeFunction("playerExit", this, chr); - } finally { - sL.unlock(); - } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + public void removePlayer(final MapleCharacter chr) { + try { + em.getIv().invokeFunction("playerExit", EventInstanceManager.this, chr); + } catch (ScriptException | NoSuchMethodException ex) { + ex.printStackTrace(); + } } public boolean isLeader(MapleCharacter chr) { @@ -1158,15 +1059,11 @@ public class EventInstanceManager { } } - public final void startEvent() { + public final synchronized void startEvent() { + eventStarted = true; + try { - sL.lock(); - try { - eventStarted = true; - em.getIv().invokeFunction("afterSetup", this); - } finally { - sL.unlock(); - } + em.getIv().invokeFunction("afterSetup", EventInstanceManager.this); } catch (ScriptException | NoSuchMethodException ex) { ex.printStackTrace(); } diff --git a/src/scripting/event/EventManager.java b/src/scripting/event/EventManager.java index 6f398cf816..9b365e95dd 100644 --- a/src/scripting/event/EventManager.java +++ b/src/scripting/event/EventManager.java @@ -60,6 +60,8 @@ import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import server.ThreadManager; +//import jdk.nashorn.api.scripting.ScriptUtils; /** * @@ -647,12 +649,11 @@ public class EventManager { if(p != null) { List lmpc; - /* - if(ServerConstants.JAVA_8) + /*if(ServerConstants.JAVA_8) { lmpc = new ArrayList<>(((Map)(ScriptUtils.convert(p, Map.class))).values()); - else + } else { lmpc = new ArrayList<>((List) p); - */ + }*/ lmpc = new ArrayList<>((List) p); party.setEligibleMembers(lmpc); @@ -809,8 +810,7 @@ public class EventManager { } private void fillEimQueue() { - Thread t = new Thread(new EventManagerWorker()); //call new thread to fill up readied instances queue - t.start(); + ThreadManager.getInstance().newTask(new EventManagerWorker()); //call new thread to fill up readied instances queue } private EventInstanceManager getReadyInstance() { diff --git a/src/scripting/event/worker/EventScriptScheduler.java b/src/scripting/event/worker/EventScriptScheduler.java index 02e4cde6db..c762ae5b07 100644 --- a/src/scripting/event/worker/EventScriptScheduler.java +++ b/src/scripting/event/worker/EventScriptScheduler.java @@ -32,6 +32,7 @@ import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import server.ThreadManager; /** * @@ -105,68 +106,62 @@ public class EventScriptScheduler { public void registerEntry(final Runnable scheduledAction, final long duration) { - Thread t = new Thread(new Runnable() { - @Override - public void run() { - schedulerLock.lock(); - try { - idleProcs = 0; - if(schedulerTask == null) { - if(disposed) return; + ThreadManager.getInstance().newTask(new Runnable() { + @Override + public void run() { + schedulerLock.lock(); + try { + idleProcs = 0; + if(schedulerTask == null) { + if(disposed) return; - schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC); - } + schedulerTask = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC); + } - registeredEntries.put(scheduledAction, Server.getInstance().getCurrentTime() + duration); - } finally { - schedulerLock.unlock(); - } - } - }); - - t.start(); + registeredEntries.put(scheduledAction, Server.getInstance().getCurrentTime() + duration); + } finally { + schedulerLock.unlock(); + } + } + }); } public void cancelEntry(final Runnable scheduledAction) { - Thread t = new Thread(new Runnable() { - @Override - public void run() { - schedulerLock.lock(); - try { - registeredEntries.remove(scheduledAction); - } finally { - schedulerLock.unlock(); - } - } - }); - - t.start(); + ThreadManager.getInstance().newTask(new Runnable() { + @Override + public void run() { + schedulerLock.lock(); + try { + registeredEntries.remove(scheduledAction); + } finally { + schedulerLock.unlock(); + } + } + }); } public void dispose() { - Thread t = new Thread(new Runnable() { - @Override - public void run() { - schedulerLock.lock(); - try { - if(schedulerTask != null) { - schedulerTask.cancel(false); - schedulerTask = null; - } + ThreadManager.getInstance().newTask(new Runnable() { + @Override + public void run() { + schedulerLock.lock(); + try { + if(schedulerTask != null) { + schedulerTask.cancel(false); + schedulerTask = null; + } - registeredEntries.clear(); - disposed = true; - } finally { - schedulerLock.unlock(); - } - - disposeLocks(); - } - }); - - t.start(); + registeredEntries.clear(); + disposed = true; + } finally { + schedulerLock.unlock(); + } + + disposeLocks(); + } + }); } private void disposeLocks() { diff --git a/src/scripting/item/ItemScriptManager.java b/src/scripting/item/ItemScriptManager.java index cacf4cafcc..c001a879e6 100644 --- a/src/scripting/item/ItemScriptManager.java +++ b/src/scripting/item/ItemScriptManager.java @@ -59,7 +59,7 @@ public class ItemScriptManager { return scriptFile.exists(); } - public void getItemScript(MapleClient c, String scriptName) { + public void runItemScript(MapleClient c, String scriptName) { if (scripts.containsKey(scriptName)) { try { scripts.get(scriptName).invokeFunction("start", new ItemScriptMethods(c)); diff --git a/src/scripting/map/MapScriptManager.java b/src/scripting/map/MapScriptManager.java index 8ffcae777a..a17239dfe0 100644 --- a/src/scripting/map/MapScriptManager.java +++ b/src/scripting/map/MapScriptManager.java @@ -62,7 +62,7 @@ public class MapScriptManager { return scriptFile.exists(); } - public void getMapScript(MapleClient c, String scriptName, boolean firstUser) { + public void runMapScript(MapleClient c, String scriptName, boolean firstUser) { if (scripts.containsKey(scriptName)) { try { scripts.get(scriptName).invokeFunction("start", new MapScriptMethods(c)); diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java index d5432dc6ef..6b729d6c8a 100644 --- a/src/server/MapleItemInformationProvider.java +++ b/src/server/MapleItemInformationProvider.java @@ -646,7 +646,7 @@ public class MapleItemInformationProvider { } public static boolean rollSuccessChance(double prop) { - return Math.random() > testYourLuck(prop / 100.0, ServerConstants.SCROLL_CHANCE_RATE); + return Math.random() >= testYourLuck(prop / 100.0, ServerConstants.SCROLL_CHANCE_RATE); } private static short getMaximumShortMaxIfOverflow(int value1, int value2) { @@ -1238,9 +1238,11 @@ public class MapleItemInformationProvider { boolean bRestricted = false; if(itemId != 0) { MapleData data = getItemData(itemId); - bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1; - if (!bRestricted) { - bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1; + if (data != null) { + bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1; + if (!bRestricted) { + bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1; + } } } diff --git a/src/server/ThreadManager.java b/src/server/ThreadManager.java new file mode 100644 index 0000000000..427881d56a --- /dev/null +++ b/src/server/ThreadManager.java @@ -0,0 +1,72 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package server; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * + * @author Ronan + */ +public class ThreadManager { + private static ThreadManager instance = new ThreadManager(); + + public static ThreadManager getInstance() { + return instance; + } + + private ThreadPoolExecutor tpe; + + private ThreadManager() {} + + private class RejectedExecutionHandlerImpl implements RejectedExecutionHandler { + + @Override + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + Thread t = new Thread(r); + t.start(); + } + + } + + public void newTask(Runnable r) { + tpe.execute(r); + } + + public void start() { + RejectedExecutionHandler reh = new RejectedExecutionHandlerImpl(); + ThreadFactory tf = Executors.defaultThreadFactory(); + + tpe = new ThreadPoolExecutor(20, 1000, 77, TimeUnit.SECONDS, new ArrayBlockingQueue(50), tf, reh); + } + + public void stop() { + tpe.shutdown(); + try { + tpe.awaitTermination(5, TimeUnit.MINUTES); + } catch (InterruptedException ie) {} + } + +} diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index e8ad6fb262..58af15616e 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -573,15 +573,28 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } + private void removeController() { + this.lockMonster(); + try { + MapleCharacter chrController = getController(); + if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster + chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId())); + chrController.stopControllingMonster(this); + } + + setController(null); + setControllerHasAggro(false); + setControllerKnowsAboutAggro(false); + } finally { + this.unlockMonster(); + } + } + public MapleCharacter killBy(final MapleCharacter killer) { distributeExperience(killer != null ? killer.getId() : 0); - - MapleCharacter chrController = getController(); - if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster - chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId())); - chrController.stopControllingMonster(this); - } - + + removeController(); + final List toSpawn = this.getRevives(); // this doesn't work (?) if (toSpawn != null) { final MapleMap reviveMap = map; @@ -647,8 +660,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } - for(int i = 8810017; i >= 8810010; i--) + for(int i = 8810017; i >= 8810010; i--) { reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true); + } } if(eim != null) { @@ -787,20 +801,24 @@ public class MapleMonster extends AbstractLoadedMapleLife { } public void switchController(MapleCharacter newController, boolean immediateAggro) { - MapleCharacter controllers = getController(); - if (controllers == newController) { - return; + this.lockMonster(); + try { + MapleCharacter controllers = getController(); + if (controllers == newController) { + return; + } + + removeController(); + + newController.controlMonster(this, immediateAggro); + setController(newController); + if (immediateAggro) { + setControllerHasAggro(true); + } + setControllerKnowsAboutAggro(false); + } finally { + this.unlockMonster(); } - if (controllers != null) { - controllers.stopControllingMonster(this); - controllers.getClient().announce(MaplePacketCreator.stopControllingMonster(getObjectId())); - } - newController.controlMonster(this, immediateAggro); - setController(newController); - if (immediateAggro) { - setControllerHasAggro(true); - } - setControllerKnowsAboutAggro(false); } public void addListener(MonsterListener listener) { @@ -863,20 +881,21 @@ public class MapleMonster extends AbstractLoadedMapleLife { } @Override - public void sendSpawnData(MapleClient c) { - if (!isAlive()) { + public void sendSpawnData(MapleClient client) { + if (hp.get() <= 0) { // mustn't monsterLock this function return; } - if (isFake()) { - c.announce(MaplePacketCreator.spawnFakeMonster(this, 0)); + if (fake) { + client.announce(MaplePacketCreator.spawnFakeMonster(this, 0)); } else { - c.announce(MaplePacketCreator.spawnMonster(this, false)); + client.announce(MaplePacketCreator.spawnMonster(this, false)); } + statiLock.lock(); try { if (stati.size() > 0) { for (final MonsterStatusEffect mse : this.stati.values()) { - c.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null)); + client.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null)); } } } finally { @@ -884,7 +903,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { } if (hasBossHPBar()) { - c.announceBossHpBar(this, this.hashCode(), makeBossHPBarPacket()); + client.announceBossHpBar(this, this.hashCode(), makeBossHPBarPacket()); } } @@ -1155,6 +1174,18 @@ public class MapleMonster extends AbstractLoadedMapleLife { map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration); } + + public void refreshMobPosition() { + updateMobPosition(getPosition()); + } + + public void updateMobPosition(Point newPoint) { + removeController(); + setPosition(newPoint); + map.broadcastMessage(MaplePacketCreator.moveMonster(this.getObjectId(), false, -1, 0, 0, 0, this.getPosition(), this.getIdleMovement())); + map.moveMonster(this, this.getPosition()); + map.updateMonsterController(this); + } private void debuffMobStat(MonsterStatus stat) { statiLock.lock(); diff --git a/src/server/life/MapleNPC.java b/src/server/life/MapleNPC.java index 7ddffac09c..a86fad4459 100644 --- a/src/server/life/MapleNPC.java +++ b/src/server/life/MapleNPC.java @@ -44,12 +44,8 @@ public class MapleNPC extends AbstractLoadedMapleLife { @Override public void sendSpawnData(MapleClient client) { - if (this.getId() > 9010010 && this.getId() < 9010014) { - client.announce(MaplePacketCreator.spawnNPCRequestController(this, false)); - } else { - client.announce(MaplePacketCreator.spawnNPC(this)); - client.announce(MaplePacketCreator.spawnNPCRequestController(this, true)); - } + client.announce(MaplePacketCreator.spawnNPC(this)); + client.announce(MaplePacketCreator.spawnNPCRequestController(this, true)); } @Override diff --git a/src/server/life/MobSkill.java b/src/server/life/MobSkill.java index 40397c234f..768e1589b4 100644 --- a/src/server/life/MobSkill.java +++ b/src/server/life/MobSkill.java @@ -103,12 +103,12 @@ public class MobSkill { this.limit = limit; } - public void applyDelayedEffect(final MapleCharacter player, final MapleMonster monster, final boolean skill, final List banishPlayers, int animationTime) { + public void applyDelayedEffect(final MapleCharacter player, final MapleMonster monster, final boolean skill, int animationTime) { Runnable toRun = new Runnable() { @Override public void run() { if(monster.isAlive()) { - applyEffect(player, monster, skill, banishPlayers); + applyEffect(player, monster, skill, null); } } }; diff --git a/src/server/maps/AbstractAnimatedMapleMapObject.java b/src/server/maps/AbstractAnimatedMapleMapObject.java index 3235a64b0a..8c54788a73 100644 --- a/src/server/maps/AbstractAnimatedMapleMapObject.java +++ b/src/server/maps/AbstractAnimatedMapleMapObject.java @@ -21,18 +21,35 @@ */ package server.maps; +import java.awt.Point; +import java.util.Collections; +import java.util.List; +import server.movement.AbsoluteLifeMovement; +import server.movement.LifeMovementFragment; + public abstract class AbstractAnimatedMapleMapObject extends AbstractMapleMapObject implements AnimatedMapleMapObject { private int stance; + @Override public int getStance() { return stance; } + @Override public void setStance(int stance) { this.stance = stance; } + @Override public boolean isFacingLeft() { return Math.abs(stance) % 2 == 1; } + + public List getIdleMovement() { + AbsoluteLifeMovement alm = new AbsoluteLifeMovement(0, getPosition(), 0, getStance()); + alm.setPixelsPerSecond(new Point(0, 0)); + + List moveUpdate = Collections.singletonList((LifeMovementFragment) alm); + return moveUpdate; + } } diff --git a/src/server/maps/MapleDoorObject.java b/src/server/maps/MapleDoorObject.java index 3dcded4632..dfad7418da 100644 --- a/src/server/maps/MapleDoorObject.java +++ b/src/server/maps/MapleDoorObject.java @@ -25,6 +25,7 @@ import client.MapleCharacter; import client.MapleClient; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.world.MapleParty; import tools.MaplePacketCreator; /** @@ -84,10 +85,9 @@ public class MapleDoorObject extends AbstractMapleMapObject { } public void warp(final MapleCharacter chr) { - boolean onParty = chr.getParty() != null; - - if (chr.getId() == ownerId || (onParty && chr.getParty().getMemberById(ownerId) != null)) { - if(!inTown() && !onParty) { + MapleParty party = chr.getParty(); + if (chr.getId() == ownerId || (party != null && party.getMemberById(ownerId) != null)) { + if(!inTown() && party == null) { chr.changeMap(to, getLinkedPortalId()); } else { chr.changeMap(to, getLinkedPortalPosition()); @@ -100,8 +100,10 @@ public class MapleDoorObject extends AbstractMapleMapObject { @Override public void sendSpawnData(MapleClient client) { - if (from.getId() == client.getPlayer().getMapId()) { - if (client.getPlayer().getParty() != null && (ownerId == client.getPlayer().getId() || client.getPlayer().getParty().getMemberById(ownerId) != null)) { + MapleCharacter chr = client.getPlayer(); + if (from.getId() == chr.getMapId()) { + MapleParty party = chr.getParty(); + if (party != null && (ownerId == chr.getId() || party.getMemberById(ownerId) != null)) { client.announce(MaplePacketCreator.partyPortal(this.getFrom().getId(), this.getTo().getId(), this.toPosition())); } @@ -112,8 +114,10 @@ public class MapleDoorObject extends AbstractMapleMapObject { @Override public void sendDestroyData(MapleClient client) { - if (from.getId() == client.getPlayer().getMapId()) { - if (client.getPlayer().getParty() != null && (ownerId == client.getPlayer().getId() || client.getPlayer().getParty().getMemberById(ownerId) != null)) { + MapleCharacter chr = client.getPlayer(); + if (from.getId() == chr.getMapId()) { + MapleParty party = chr.getParty(); + if (party != null && (ownerId == chr.getId() || party.getMemberById(ownerId) != null)) { client.announce(MaplePacketCreator.partyPortal(999999999, 999999999, new Point(-1, -1))); } client.announce(MaplePacketCreator.removeDoor(ownerId, inTown())); diff --git a/src/server/maps/MapleDragon.java b/src/server/maps/MapleDragon.java index b889e88833..6632b91940 100644 --- a/src/server/maps/MapleDragon.java +++ b/src/server/maps/MapleDragon.java @@ -35,8 +35,8 @@ public class MapleDragon extends AbstractAnimatedMapleMapObject { super(); this.owner = chr; this.setPosition(chr.getPosition()); - this.setStance(chr.getStance()); - sendSpawnData(chr.getClient()); + this.setStance(chr.getStance()); + this.sendSpawnData(chr.getClient()); } @Override @@ -45,8 +45,8 @@ public class MapleDragon extends AbstractAnimatedMapleMapObject { } @Override - public void sendSpawnData(MapleClient c) { - c.announce(MaplePacketCreator.spawnDragon(this)); + public void sendSpawnData(MapleClient client) { + client.announce(MaplePacketCreator.spawnDragon(this)); } @Override diff --git a/src/server/maps/MapleHiredMerchant.java b/src/server/maps/MapleHiredMerchant.java index 13d6113054..4db00db6d0 100644 --- a/src/server/maps/MapleHiredMerchant.java +++ b/src/server/maps/MapleHiredMerchant.java @@ -645,15 +645,14 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { return mesos; } - @Override - public void sendDestroyData(MapleClient client) { - } - @Override public MapleMapObjectType getType() { return MapleMapObjectType.HIRED_MERCHANT; } - + + @Override + public void sendDestroyData(MapleClient client) {} + @Override public void sendSpawnData(MapleClient client) { client.announce(MaplePacketCreator.spawnHiredMerchantBox(this)); diff --git a/src/server/maps/MapleKite.java b/src/server/maps/MapleKite.java index 0ee937d625..d0cea46a48 100644 --- a/src/server/maps/MapleKite.java +++ b/src/server/maps/MapleKite.java @@ -3,7 +3,6 @@ package server.maps; import java.awt.Point; import client.MapleCharacter; import client.MapleClient; -import constants.ServerConstants; import tools.MaplePacketCreator; public class MapleKite extends AbstractMapleMapObject { diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index d7fd94401e..086b72f63f 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -1552,9 +1552,11 @@ public class MapleMap { if (!monster.isAlive()) { return; } - if (monster.getController() != null) { - if (monster.getController().getMap() != this) { - monster.getController().stopControllingMonster(monster); + + MapleCharacter chrController = monster.getController(); + if (chrController != null) { + if (chrController.getMap() != this) { + chrController.stopControllingMonster(monster); } else { return; } @@ -1574,7 +1576,7 @@ public class MapleMap { } finally { chrRLock.unlock(); } - if (newController != null) {// was a new controller found? (if not no one is on the map) + if (newController != null) { // was a new controller found? (if not no one is on the map) if (monster.isFirstAttack()) { newController.controlMonster(monster, true); monster.setControllerHasAggro(true); @@ -1588,6 +1590,15 @@ public class MapleMap { } } + private Map getCopyMapObjects() { + objectRLock.lock(); + try { + return new HashMap<>(mapobjects); + } finally { + objectRLock.unlock(); + } + } + public List getMapObjects() { objectRLock.lock(); try { @@ -2340,14 +2351,14 @@ public class MapleMap { if (onFirstUserEnter.length() != 0 && !chr.hasEntered(onFirstUserEnter, mapid) && MapScriptManager.getInstance().scriptExists(onFirstUserEnter, true)) { chr.enteredScript(onFirstUserEnter, mapid); - MapScriptManager.getInstance().getMapScript(chr.getClient(), onFirstUserEnter, true); + MapScriptManager.getInstance().runMapScript(chr.getClient(), onFirstUserEnter, true); } } if (onUserEnter.length() != 0) { if (onUserEnter.equals("cygnusTest") && (mapid < 913040000 || mapid > 913040006)) { chr.saveLocation("INTRO"); } - MapScriptManager.getInstance().getMapScript(chr.getClient(), onUserEnter, false); + MapScriptManager.getInstance().runMapScript(chr.getClient(), onUserEnter, false); } if (FieldLimit.CANNOTUSEMOUNTS.check(fieldLimit) && chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) { chr.cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING); @@ -2629,10 +2640,15 @@ public class MapleMap { } for (MapleMonster monster : chr.getControlledMonsters()) { - monster.setController(null); - monster.setControllerHasAggro(false); - monster.setControllerKnowsAboutAggro(false); - updateMonsterController(monster); + monster.lockMonster(); + try { + monster.setController(null); + monster.setControllerHasAggro(false); + monster.setControllerKnowsAboutAggro(false); + updateMonsterController(monster); + } finally { + monster.unlockMonster(); + } } chr.leaveMap(); @@ -3042,7 +3058,7 @@ public class MapleMap { } private static void updateMapObjectVisibility(MapleCharacter chr, MapleMapObject mo) { - if (!chr.isMapObjectVisible(mo)) { // item entered view range + if (!chr.isMapObjectVisible(mo)) { // object entered view range if (mo.getType() == MapleMapObjectType.SUMMON || mo.getPosition().distanceSq(chr.getPosition()) <= getRangedDistance()) { chr.addVisibleMapObject(mo); mo.sendSpawnData(chr.getClient()); @@ -3055,27 +3071,22 @@ public class MapleMap { public void moveMonster(MapleMonster monster, Point reportedPos) { monster.setPosition(reportedPos); - chrRLock.lock(); - try { - for (MapleCharacter chr : characters) { - updateMapObjectVisibility(chr, monster); - } - } finally { - chrRLock.unlock(); + for (MapleCharacter chr : getAllPlayers()) { + updateMapObjectVisibility(chr, monster); } } public void movePlayer(MapleCharacter player, Point newPosition) { player.setPosition(newPosition); - Collection visibleObjects = player.getVisibleMapObjects(); - objectRLock.lock(); try { + Collection visibleObjects = player.getVisibleMapObjects(); MapleMapObject[] visibleObjectsNow = visibleObjects.toArray(new MapleMapObject[visibleObjects.size()]); - + + Map mapObjects = getCopyMapObjects(); for (MapleMapObject mo : visibleObjectsNow) { if (mo != null) { - if (mapobjects.get(mo.getObjectId()) == mo) { + if (mapObjects.get(mo.getObjectId()) == mo) { updateMapObjectVisibility(player, mo); } else { player.removeVisibleMapObject(mo); @@ -3084,8 +3095,6 @@ public class MapleMap { } } catch (Exception e) { e.printStackTrace(); - } finally { - objectRLock.unlock(); } for (MapleMapObject mo : getMapObjectsInRange(player.getPosition(), getRangedDistance(), rangedMapobjectTypes)) { diff --git a/src/server/maps/MapleSummon.java b/src/server/maps/MapleSummon.java index d5d0c6175b..5a1a677c25 100644 --- a/src/server/maps/MapleSummon.java +++ b/src/server/maps/MapleSummon.java @@ -47,11 +47,12 @@ public class MapleSummon extends AbstractAnimatedMapleMapObject { setPosition(pos); } + @Override public void sendSpawnData(MapleClient client) { - if (this != null) client.announce(MaplePacketCreator.spawnSummon(this, false)); - + client.announce(MaplePacketCreator.spawnSummon(this, false)); } + @Override public void sendDestroyData(MapleClient client) { client.announce(MaplePacketCreator.removeSummon(this, true)); } diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 6ce86148ec..56de91e59d 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -877,12 +877,28 @@ public class MaplePacketCreator { * * @param c The MapleClient to load characters of. * @param serverId The ID of the server requested. + * @param status The charlist request result. * @return The character list packet. + * + * Possible values for status: + *
2: ID deleted or blocked
+ *
3: ID deleted or blocked
+ *
4: Incorrect password
+ *
5: Not an registered ID
+ *
6: Trouble logging in?
+ *
10: Server handling too many connections
+ *
11: Only 20 years or older
+ *
13: Unable to log as master at IP
+ *
14: Wrong gateway or personal info
+ *
15: Still processing request
+ *
16: Verify account via email
+ *
17: Wrong gateway or personal info
+ *
21: Verify account via email
*/ - public static byte[] getCharList(MapleClient c, int serverId) { + public static byte[] getCharList(MapleClient c, int serverId, int status) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.CHARLIST.getValue()); - mplew.write(0); + mplew.write(status); List chars = c.loadCharacters(serverId); mplew.write((byte) chars.size()); for (MapleCharacter chr : chars) { @@ -1425,7 +1441,6 @@ public class MaplePacketCreator { mplew.write(1); mplew.writeInt(life.getObjectId()); return mplew.getPacket(); - //return spawnMonsterInternal(life, true, false, false, 0, false); } /** @@ -2140,11 +2155,11 @@ public class MaplePacketCreator { MapleCharacter targetChr = target.getPlayer(); if (targetChr != null && targetChr.getPartnerId() == chr.getId()) { - mplew.writeInt(0); // 1a pessoa: ser 0 0 implica match... - mplew.writeInt(0); // 3a pessoa: 1 2 2 1 funciona! + mplew.writeInt(0); + mplew.writeInt(0); } else { - mplew.writeInt(chr.getId()); // 1a pessoa: ser 0 0 implica match... - mplew.writeInt(ring.getPartnerChrId()); // 3a pessoa: 1 2 2 1 funciona! + mplew.writeInt(chr.getId()); + mplew.writeInt(ring.getPartnerChrId()); } mplew.writeInt(ring.getItemId()); @@ -2386,6 +2401,18 @@ public class MaplePacketCreator { } } } + + public static byte[] throwGrenade(int cid, Point p, int keyDown, int skillId, int skillLevel) { // packets found thanks to GabrielSin + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.THROW_GRENADE.getValue()); + mplew.writeInt(cid); + mplew.writeInt(p.x); + mplew.writeInt(p.y); + mplew.writeInt(keyDown); + mplew.writeInt(skillId); + mplew.writeInt(skillLevel); + return mplew.getPacket(); + } // someone thought it was a good idea to handle floating point representation through packets ROFL private static int doubleToShortBits(double d) { @@ -4765,19 +4792,26 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] commandResponse(int cid, byte index, int animation, boolean success) { - //AE 00 01 00 00 00 00 01 00 00 + public static byte[] petFoodResponse(int cid, byte index, boolean success, boolean balloonType) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.PET_COMMAND.getValue()); mplew.writeInt(cid); mplew.write(index); - mplew.write((animation == 1 || !success) ? 1 : 0); + mplew.write(1); + mplew.writeBool(success); + mplew.writeBool(balloonType); + return mplew.getPacket(); + } + + public static byte[] commandResponse(int cid, byte index, boolean talk, int animation, boolean balloonType) { + final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.PET_COMMAND.getValue()); + mplew.writeInt(cid); + mplew.write(index); + mplew.write(0); mplew.write(animation); - if (animation == 1) { - mplew.write(0); - } else { - mplew.writeShort(success ? 1 : 0); - } + mplew.writeBool(!talk); + mplew.writeBool(balloonType); return mplew.getPacket(); } diff --git a/wz/Item.wz/Etc/0400.img.xml b/wz/Item.wz/Etc/0400.img.xml index 7951de70e4..0c1eb6aa7d 100644 --- a/wz/Item.wz/Etc/0400.img.xml +++ b/wz/Item.wz/Etc/0400.img.xml @@ -8438,6 +8438,7 @@ + diff --git a/wz/Map.wz/Map/Map2/200010000.img.xml b/wz/Map.wz/Map/Map2/200010000.img.xml index 805f91cfc0..b50e388fae 100644 --- a/wz/Map.wz/Map/Map2/200010000.img.xml +++ b/wz/Map.wz/Map/Map2/200010000.img.xml @@ -775,20 +775,6 @@ - - - - - - - - - - - - - - diff --git a/wz/Map.wz/Map/Map2/240010200.img.xml b/wz/Map.wz/Map/Map2/240010200.img.xml index 9b58c5c638..8b81cb2b7d 100644 --- a/wz/Map.wz/Map/Map2/240010200.img.xml +++ b/wz/Map.wz/Map/Map2/240010200.img.xml @@ -623,20 +623,6 @@ - - - - - - - - - - - - - - diff --git a/wz/Map.wz/Map/Map2/261000000.img.xml b/wz/Map.wz/Map/Map2/261000000.img.xml index d86ffae50e..301d4ce4c9 100644 --- a/wz/Map.wz/Map/Map2/261000000.img.xml +++ b/wz/Map.wz/Map/Map2/261000000.img.xml @@ -9329,42 +9329,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -10259,23 +10223,23 @@ - + - + - + - - + + - + @@ -10283,591 +10247,591 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + @@ -10875,97 +10839,97 @@ - + - - + + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - - + + - + - + - + - + - + @@ -10973,15 +10937,15 @@ - + - + - + @@ -10989,39 +10953,39 @@ - + - + - + - + - + - + - + - + - + @@ -11029,687 +10993,687 @@ - + - - + + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - + - + @@ -11717,207 +11681,207 @@ - + - + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + @@ -11925,119 +11889,119 @@ - + - - + + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + @@ -12045,687 +12009,687 @@ - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - - + + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - + - + @@ -12733,121 +12697,121 @@ - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + @@ -12855,111 +12819,111 @@ - + - + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + @@ -12967,556 +12931,584 @@ - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - - + + - + - + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -14049,422 +14041,390 @@ - + - - - - - - - - - - - - - - - - - + - + - - + + - - - - - - - - - - - - - - - - - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + @@ -14511,134 +14471,134 @@ - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - - + + - + - + - + - - + + - + - - + + - + - - + + - + - + @@ -14943,406 +14903,406 @@ - + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + @@ -15359,7 +15319,7 @@ - + @@ -15371,7 +15331,7 @@ - + @@ -15383,7 +15343,7 @@ - + @@ -15419,7 +15379,7 @@ - + @@ -15431,7 +15391,7 @@ - + @@ -15455,7 +15415,7 @@ - + @@ -15517,7 +15477,7 @@ - + @@ -15530,7 +15490,7 @@ - + @@ -15555,7 +15515,7 @@ - + @@ -15568,7 +15528,7 @@ - + @@ -15580,7 +15540,7 @@ - + diff --git a/wz/Map.wz/Map/Map6/600000000.img.xml b/wz/Map.wz/Map/Map6/600000000.img.xml index 34ff620cb8..46147881a5 100644 --- a/wz/Map.wz/Map/Map6/600000000.img.xml +++ b/wz/Map.wz/Map/Map6/600000000.img.xml @@ -11642,8 +11642,8 @@ - - + + @@ -11678,8 +11678,8 @@ - - + + @@ -11750,8 +11750,8 @@ - - + + @@ -11929,5 +11929,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Map.wz/Map/Map8/801000000.img.xml b/wz/Map.wz/Map/Map8/801000000.img.xml index 87eaefcf29..c5a1659c52 100644 --- a/wz/Map.wz/Map/Map8/801000000.img.xml +++ b/wz/Map.wz/Map/Map8/801000000.img.xml @@ -1,4060 +1,18 @@ - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4064,161 +22,4196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Quest.wz/Act.img.xml b/wz/Quest.wz/Act.img.xml index 570a733968..565a5f4226 100644 --- a/wz/Quest.wz/Act.img.xml +++ b/wz/Quest.wz/Act.img.xml @@ -34701,14 +34701,12 @@ - - diff --git a/wz/Quest.wz/QuestInfo.img.xml b/wz/Quest.wz/QuestInfo.img.xml index 2fd7e02fe6..d794b9c56d 100644 --- a/wz/Quest.wz/QuestInfo.img.xml +++ b/wz/Quest.wz/QuestInfo.img.xml @@ -12712,7 +12712,7 @@ Can proceed to the "The Secrets Behind the Ring?" quest. - + diff --git a/wz/Skill.wz/1310.img.xml b/wz/Skill.wz/1310.img.xml index 0283d93118..98dece9e57 100644 --- a/wz/Skill.wz/1310.img.xml +++ b/wz/Skill.wz/1310.img.xml @@ -1028,6 +1028,7 @@ + @@ -1039,6 +1040,7 @@ + @@ -1050,6 +1052,7 @@ + @@ -1061,6 +1064,7 @@ + @@ -1072,6 +1076,7 @@ + @@ -1083,6 +1088,7 @@ + @@ -1094,6 +1100,7 @@ + @@ -1105,6 +1112,7 @@ + @@ -1116,6 +1124,7 @@ + @@ -1127,6 +1136,7 @@ + @@ -1138,6 +1148,7 @@ + @@ -1149,6 +1160,7 @@ + @@ -1160,6 +1172,7 @@ + @@ -1171,6 +1184,7 @@ + @@ -1182,6 +1196,7 @@ + @@ -1193,6 +1208,7 @@ + @@ -1204,6 +1220,7 @@ + @@ -1215,6 +1232,7 @@ + @@ -1226,6 +1244,7 @@ + @@ -1237,6 +1256,7 @@ + diff --git a/wz/Skill.wz/1311.img.xml b/wz/Skill.wz/1311.img.xml index db05173de3..a7b0597f09 100644 --- a/wz/Skill.wz/1311.img.xml +++ b/wz/Skill.wz/1311.img.xml @@ -1633,121 +1633,121 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 070f3b9027..010b4255ca 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -8181,7 +8181,7 @@ - +