From 94425ba616dacf61b156044ac3993e2d06584c8f Mon Sep 17 00:00:00 2001 From: ronancpl Date: Sat, 30 Jun 2018 22:48:02 -0300 Subject: [PATCH] EXP system & Mob buffs/diseases optimization Solved a problem within EXP distribution system that would hand out less overall EXP than the expected when the amount to be earned is low. Optimized mob buffs and diseases, now using a dedicated thread to process all status expirations on a batch. Refactored MonitoredLockTypes names to something more easily identificable. Added a delay on mob effect applications, to be registered in after the cast animation time. Fixed Flame Thrower acting passively when a attacking skill is used by the player. --- .gitignore | 2 +- docs/issues.txt | 1 + docs/mychanges_ptbr.txt | 12 +- handbook/Etc.txt | 2 +- scripts/npc/1012100.js | 2 +- scripts/npc/1022000.js | 2 +- scripts/npc/1032001.js | 2 +- scripts/npc/1052001.js | 2 +- scripts/npc/1090000.js | 2 +- scripts/npc/1092090.js | 52 ++-- scripts/npc/1092091.js | 53 ++-- scripts/npc/1092094.js | 43 ++- scripts/npc/1092095.js | 42 ++- scripts/npc/9977777.js | 3 + scripts/quest/20101.js | 4 +- scripts/quest/20102.js | 4 +- scripts/quest/20103.js | 4 +- scripts/quest/20104.js | 4 +- scripts/quest/20105.js | 4 +- scripts/quest/4647.js | 2 +- scripts/reactor/8001000.js | 2 +- sql/db_database.sql | 14 +- sql/db_drops.sql | 46 +-- src/client/MapleCharacter.java | 8 +- src/client/MapleClient.java | 4 +- src/client/status/MonsterStatusEffect.java | 30 +- src/constants/ServerConstants.java | 4 + src/net/server/Server.java | 4 +- src/net/server/audit/ThreadTracker.java | 2 +- src/net/server/channel/Channel.java | 35 +++ .../handlers/AbstractDealDamageHandler.java | 18 +- .../channel/handlers/MoveLifeHandler.java | 18 +- .../channel/worker/MobStatusScheduler.java | 148 ++++++++++ src/net/server/worker/BaseWorker.java | 2 +- src/net/server/world/World.java | 2 +- src/scripting/AbstractPlayerInteraction.java | 19 ++ src/scripting/AbstractScriptManager.java | 2 +- src/scripting/item/ItemScriptManager.java | 11 +- src/scripting/map/MapScriptManager.java | 12 +- src/scripting/portal/PortalScriptManager.java | 7 + src/server/life/MapleLifeFactory.java | 19 +- src/server/life/MapleMonster.java | 89 +++--- .../life/MapleMonsterInformationProvider.java | 20 +- src/server/life/MobSkill.java | 30 +- src/server/maps/MapleMap.java | 55 ++-- src/server/maps/MapleReactor.java | 2 +- src/tools/MaplePacketCreator.java | 7 +- src/tools/locks/MonitoredLockType.java | 109 ++++--- .../dist/MapleQuestItemFetcher.jar | Bin 145072 -> 145037 bytes .../MapleQuestItemFetcher/lib/QuestReport.txt | 2 +- .../MapleQuestItemFetcher.java | 27 +- wz/Item.wz/Etc/0403.img.xml | 10 +- wz/Map.wz/Map/Map1/101000301.img.xml | 2 +- wz/Quest.wz/Act.img.xml | 268 ++++++++++-------- wz/Quest.wz/Check.img.xml | 258 +++++++++-------- wz/Quest.wz/QuestInfo.img.xml | 2 +- wz/String.wz/Etc.img.xml | 4 +- 57 files changed, 926 insertions(+), 608 deletions(-) create mode 100644 src/net/server/channel/worker/MobStatusScheduler.java diff --git a/.gitignore b/.gitignore index ca7e2ab393..c635df16b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/logs/** +/logs/** .idea/ # build files diff --git a/docs/issues.txt b/docs/issues.txt index 6f68216c1f..d28e49a516 100644 --- a/docs/issues.txt +++ b/docs/issues.txt @@ -13,6 +13,7 @@ Known issues: - If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen. - Sometimes battleship may behave oddly with the enhanced buff system, making the character d/c in certain scenarios. - Dragon Roar doesn't show the stun effect to players. +- Some monster status such as freeze and weapon/magic reflect doesn't behave properly in certain scenarios. Freeze seems to not work on mobs with low OID or are starters from server boot time. --------------------------- --------------------------- diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 02f36dc2e2..285c1bb371 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -1083,4 +1083,14 @@ Adicionado efeito de dispel de weapondef e magicdef up em mobs ao atacar com Sha Corrigido Shadow Meso consumindo projetil (deveria usar meso somente). Adicionado recompensa de buff ao completar o HolidayPQ. Adicionado drops de itens que dão buffs característicos nos reatores da Zakum Prequest. -Scroll shop custom mudou para a NPC Asia em Neo City (anteriormente estava no Spindle). \ No newline at end of file +Scroll shop custom mudou para a NPC Asia em Neo City (anteriormente estava no Spindle). + +27 Junho 2018, +Resolvido um problema no sistema de ganho de EXP que daria ao jogador um valor abaixo do que seria o normal nos casos onde o EXP ganho é baixo. +Otimizado mob buffs e diseases agora utilizando thread dedicada para lidar com a duração, anteriormente eram criadas novas schedules pra cada instância. +Refatorado MonitoredLockTypes para agora dar um label específico a cada lock do sistema. + +29 - 30 Junho 2018, +Corrigido quest Milk Jug com NPCs trocados. +Adicionado um delay na aplicação de efeitos dos skills de mobs, buffs e demais efeitos agora são registrados após o tempo da animação. +Corrigido Flame Thrower atuando passivamente quando o jogador usa uma skill de ataque. \ No newline at end of file diff --git a/handbook/Etc.txt b/handbook/Etc.txt index 61d29e4940..903b1ba09b 100644 --- a/handbook/Etc.txt +++ b/handbook/Etc.txt @@ -241,7 +241,7 @@ 4000172 - Three-Tailed Foxtail - A soft foxtail that Three-Tailed Fox removed from itself. 4000173 - Broom - A small broom made from bushes that Blins dropped. 4000174 - Money Envelope - You can sell this envelope for 10,000 meso in the store. -4000175 - Minature Pianus - A miniature version of Pianus +4000175 - Miniature Pianus - A miniature version of Pianus 4000176 - Poisonous Mushroom - A poisonous mushroom that lives on the humongous Zombie Mushroom. 4000177 - Mixed Block - A block from Mix Golem that consists of its chest area. 4000178 - Iron Boar Armor - A small piece of the solid armor from Iron Boar. diff --git a/scripts/npc/1012100.js b/scripts/npc/1012100.js index 8063e7a86d..a132cfce17 100644 --- a/scripts/npc/1012100.js +++ b/scripts/npc/1012100.js @@ -49,7 +49,7 @@ function start() { if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType)) cm.sendNext("So you decided to become a #rBowman#k?"); else { - cm.sendOk("Train a bit more and I can show you the way of the #rBowman#k."); + cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rBowman#k."); cm.dispose(); } } else if (cm.getLevel() >= 30 && cm.getJobId() == 300) { diff --git a/scripts/npc/1022000.js b/scripts/npc/1022000.js index 231c841844..a112e93b0c 100644 --- a/scripts/npc/1022000.js +++ b/scripts/npc/1022000.js @@ -50,7 +50,7 @@ function start() { if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType)) cm.sendNext("Do you want to become a Warrior? You need to meet some criteria in order to do so.#b You should be at least in level 10, with at least 35 in STR#k. Let's see..."); else { - cm.sendOk("Train a bit more and I can show you the way of the #rWarrior#k."); + cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rWarrior#k."); cm.dispose(); } } else if (cm.getLevel() >= 30 && cm.getJobId() == 100) { diff --git a/scripts/npc/1032001.js b/scripts/npc/1032001.js index 527b31602a..993eb304fc 100644 --- a/scripts/npc/1032001.js +++ b/scripts/npc/1032001.js @@ -50,7 +50,7 @@ function start() { if (cm.getLevel() >= 8 && cm.canGetFirstJob(jobType)) cm.sendNext("Want to be a magician? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 8#k, with getting INT as your top priority. Let's see."); else { - cm.sendOk("Train a bit more and I can show you the way of the #rMagician#k."); + cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rMagician#k."); cm.dispose(); } } else if (cm.getLevel() >= 30 && cm.getJobId() == 200) { diff --git a/scripts/npc/1052001.js b/scripts/npc/1052001.js index 7ef886ac0a..3f76c7fb14 100644 --- a/scripts/npc/1052001.js +++ b/scripts/npc/1052001.js @@ -49,7 +49,7 @@ function start() { if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType)) cm.sendNext("Want to be a thief? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10, with your DEX over 25#k. Let's see."); else { - cm.sendOk("Train a bit more and I can show you the way of the #rThief#k."); + cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rThief#k."); cm.dispose(); } } else if (cm.getLevel() >= 30 && cm.getJobId() == 400) { diff --git a/scripts/npc/1090000.js b/scripts/npc/1090000.js index c0163e07f4..575c7aaffb 100644 --- a/scripts/npc/1090000.js +++ b/scripts/npc/1090000.js @@ -49,7 +49,7 @@ function start() { if (cm.getLevel() >= 10 && cm.canGetFirstJob(jobType)) cm.sendNext("Want to be a pirate? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10#k. Let's see."); else { - cm.sendOk("Train a bit more and I can show you the way of the #rPirate#k."); + cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rPirate#k."); cm.dispose(); } } else if (cm.getLevel() >= 30 && cm.getJobId() == 500) { diff --git a/scripts/npc/1092090.js b/scripts/npc/1092090.js index ed89201800..2dac576325 100644 --- a/scripts/npc/1092090.js +++ b/scripts/npc/1092090.js @@ -19,34 +19,34 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -status = -1; function start() { - if(cm.haveItem(4031847)) - cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty..."); - else if(cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)){ - cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty."); - if(cm.haveItem(4031848)) - cm.gainItem(4031848,-1); - else if(cm.haveItem(4031849)) - cm.gainItem(4031849, -1); - else - cm.gainItem(4031850, -1); - cm.gainItem(4031847, 1); + if(cm.getQuestProgress(2180, 0) == 1) { + cm.sendNext("You have taken milk from this cow recently, check another cow."); cm.dispose(); + return; } -} - -function action(mode, type, selection){ - if(mode == -1) - cm.dispose(); - else if(mode == 0){ - status--; - start(); - }else - status++; - if(status == 0) - cm.sendPrev("The hungry calf isn't interested in the empty bottle."); - else if(status == 1) - cm.dispose(); + + if (cm.canHold(4031848) && cm.haveItem(4031847)) { + cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk."); + cm.gainItem(4031847, -1); + cm.gainItem(4031848, 1); + + cm.setQuestProgress(2180, 0, 1); + } else if (cm.canHold(4031849, 1) && cm.haveItem(4031848)) { + cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk."); + cm.gainItem(4031848, -1); + cm.gainItem(4031849, 1); + + cm.setQuestProgress(2180, 0, 1); + } else if (cm.canHold(4031850) && cm.haveItem(4031849)) { + cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk."); + cm.gainItem(4031849, -1); + cm.gainItem(4031850, 1); + + cm.setQuestProgress(2180, 0, 1); + } else { + cm.sendNext("Your inventory is full, and there's no room for a milk bottle."); + } + cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/1092091.js b/scripts/npc/1092091.js index f6e83bfc2b..5d5db97d54 100644 --- a/scripts/npc/1092091.js +++ b/scripts/npc/1092091.js @@ -20,34 +20,33 @@ along with this program. If not, see . */ -status = -1; - function start() { - if (cm.haveItem(4031847)) - cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty..."); - else if (cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)) { - cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty."); - if (cm.haveItem(4031848)) - cm.gainItem(4031848,-1); - else if (cm.haveItem(4031849)) - cm.gainItem(4031849, -1); - else - cm.gainItem(4031850, -1); - cm.gainItem(4031847, 1); + if(cm.getQuestProgress(2180, 0) == 2) { + cm.sendNext("You have taken milk from this cow recently, check another cow."); cm.dispose(); + return; } -} - -function action(mode, type, selection){ - if (mode == -1) - cm.dispose(); - else if (mode == 0) { - status--; - start(); - } else - status++; - if (status == 0) - cm.sendPrev("The hungry calf isn't interested in the empty bottle."); - else if (status == 1) - cm.dispose(); + + if (cm.canHold(4031848) && cm.haveItem(4031847)){ + cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk."); + cm.gainItem(4031847, -1); + cm.gainItem(4031848, 1); + + cm.setQuestProgress(2180, 0, 2); + } else if(cm.canHold(4031849) && cm.haveItem(4031848)){ + cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk."); + cm.gainItem(4031848, -1); + cm.gainItem(4031849, 1); + + cm.setQuestProgress(2180, 0, 2); + } else if(cm.canHold(4031850) && cm.haveItem(4031849)){ + cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk."); + cm.gainItem(4031849, -1); + cm.gainItem(4031850, 1); + + cm.setQuestProgress(2180, 0, 2); + } else { + cm.sendNext("Your inventory is full, and there's no room for a milk bottle."); + } + cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/1092094.js b/scripts/npc/1092094.js index 27d7a951b4..ed89201800 100644 --- a/scripts/npc/1092094.js +++ b/scripts/npc/1092094.js @@ -19,21 +19,34 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +status = -1; function start() { - if (cm.canHold(4031848) && cm.haveItem(4031847)) { - cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk."); - cm.gainItem(4031847, -1); - cm.gainItem(4031848, 1); - } else if (cm.canHold(4031849, 1) && cm.haveItem(4031848)) { - cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk."); - cm.gainItem(4031848, -1); - cm.gainItem(4031849, 1); - } else if (cm.canHold(4031850) && cm.haveItem(4031849)) { - cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk."); - cm.gainItem(4031849, -1); - cm.gainItem(4031850, 1); - } else - cm.sendNext("Your inventory is full, and there's no room for a milk bottle."); - cm.dispose(); + if(cm.haveItem(4031847)) + cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty..."); + else if(cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)){ + cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty."); + if(cm.haveItem(4031848)) + cm.gainItem(4031848,-1); + else if(cm.haveItem(4031849)) + cm.gainItem(4031849, -1); + else + cm.gainItem(4031850, -1); + cm.gainItem(4031847, 1); + cm.dispose(); + } +} + +function action(mode, type, selection){ + if(mode == -1) + cm.dispose(); + else if(mode == 0){ + status--; + start(); + }else + status++; + if(status == 0) + cm.sendPrev("The hungry calf isn't interested in the empty bottle."); + else if(status == 1) + cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/1092095.js b/scripts/npc/1092095.js index f3b9a6dca8..f6e83bfc2b 100644 --- a/scripts/npc/1092095.js +++ b/scripts/npc/1092095.js @@ -20,20 +20,34 @@ along with this program. If not, see . */ +status = -1; + function start() { - if (cm.canHold(4031848) && cm.haveItem(4031847)){ - cm.sendNext("Now filling up the bottle with milk. The bottle is now 1/3 full of milk."); - cm.gainItem(4031847, -1); - cm.gainItem(4031848, 1); - } else if(cm.canHold(4031849) && cm.haveItem(4031848)){ - cm.sendNext("Now filling up the bottle with milk. The bottle is now 2/3 full of milk."); - cm.gainItem(4031848, -1); - cm.gainItem(4031849, 1); - } else if(cm.canHold(4031850) && cm.haveItem(4031849)){ - cm.sendNext("Now filling up the bottle with milk. The bottle is now completely full of milk."); - cm.gainItem(4031849, -1); - cm.gainItem(4031850, 1); + if (cm.haveItem(4031847)) + cm.sendNext("The hungry calf is drinking all the milk! The bottle remains empty..."); + else if (cm.haveItem(4031848) || cm.haveItem(4031849) || cm.haveItem(4031850)) { + cm.sendNext("The hungry calf is drinking all the milk! The bottle is now empty."); + if (cm.haveItem(4031848)) + cm.gainItem(4031848,-1); + else if (cm.haveItem(4031849)) + cm.gainItem(4031849, -1); + else + cm.gainItem(4031850, -1); + cm.gainItem(4031847, 1); + cm.dispose(); + } +} + +function action(mode, type, selection){ + if (mode == -1) + cm.dispose(); + else if (mode == 0) { + status--; + start(); } else - cm.sendNext("Your inventory is full, and there's no room for a milk bottle."); - cm.dispose(); + status++; + if (status == 0) + cm.sendPrev("The hungry calf isn't interested in the empty bottle."); + else if (status == 1) + cm.dispose(); } \ No newline at end of file diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js index d0f427fbd3..b35a45ff68 100644 --- a/scripts/npc/9977777.js +++ b/scripts/npc/9977777.js @@ -144,6 +144,7 @@ function writeFeatureTab_Serverpotentials() { addFeature("Enhanced auto-pot system: smart pet potion handle."); addFeature("Enhanced buff system: best buffs effects takes place."); addFeature("Enhanced AP auto-assigner: focus on eqp demands."); + addFeature("Consistent experience gain system."); addFeature("NPC crafters won't take items freely anymore."); addFeature("Duey: pkg rcvd popup and many delivery mechanics."); addFeature("Pet pickup gives preference to player attacks."); @@ -169,6 +170,7 @@ function writeFeatureTab_AdminGMcommands() { function writeFeatureTab_CustomNPCs() { addFeature("Spiegelmann: automatized rock-refiner."); + addFeature("Asia: scroll & rarities shop NPC."); addFeature("Abdula: lists droppers of needed skill/mastery books."); addFeature("Agent E: accessory crafter."); addFeature("Donation Box: automatized item-buyer."); @@ -197,6 +199,7 @@ function writeFeatureTab_Project() { addFeature("Reviewed many Java aspects that needed attention."); addFeature("Reviewed SQL data, eliminating duplicated entries."); addFeature("Protected many flaws with login management system."); + addFeature("Developed many survey tools for content management."); addFeature("ThreadTracker: runtime tool for deadlock detection."); addFeature("Heavily reviewed future task management, spawning much less threads and relieving task overload on the TimerManager."); } diff --git a/scripts/quest/20101.js b/scripts/quest/20101.js index 895361f630..f6d2107782 100644 --- a/scripts/quest/20101.js +++ b/scripts/quest/20101.js @@ -22,8 +22,8 @@ function end(mode, type, selection) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Dawn Warrior?"); } else if (status == 1) { if(!qm.canGetFirstJob(jobType)) { - cm.sendOk("Train a bit more and I can show you the way of the #rDawn Warrior#k."); - cm.dispose(); + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rDawn Warrior#k."); + qm.dispose(); return; } diff --git a/scripts/quest/20102.js b/scripts/quest/20102.js index a32faea5d3..f4bd37ddcb 100644 --- a/scripts/quest/20102.js +++ b/scripts/quest/20102.js @@ -22,8 +22,8 @@ function end(mode, type, selection) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Blaze Wizard?"); } else if (status == 1) { if(!qm.canGetFirstJob(jobType)) { - cm.sendOk("Train a bit more and I can show you the way of the #rBlaze Wizard#k."); - cm.dispose(); + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rBlaze Wizard#k."); + qm.dispose(); return; } diff --git a/scripts/quest/20103.js b/scripts/quest/20103.js index 223632f608..03d54ab4e8 100644 --- a/scripts/quest/20103.js +++ b/scripts/quest/20103.js @@ -22,8 +22,8 @@ function end(mode, type, selection) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Wind Archer?"); } else if (status == 1) { if(!qm.canGetFirstJob(jobType)) { - cm.sendOk("Train a bit more and I can show you the way of the #rWind Archer#k."); - cm.dispose(); + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rWind Archer#k."); + qm.dispose(); return; } diff --git a/scripts/quest/20104.js b/scripts/quest/20104.js index 6e1ef777e5..a0fc3311a0 100644 --- a/scripts/quest/20104.js +++ b/scripts/quest/20104.js @@ -22,8 +22,8 @@ function end(mode, type, selection) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Night Walker?"); } else if (status == 1) { if(!qm.canGetFirstJob(jobType)) { - cm.sendOk("Train a bit more and I can show you the way of the #rNight Walker#k."); - cm.dispose(); + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rNight Walker#k."); + qm.dispose(); return; } diff --git a/scripts/quest/20105.js b/scripts/quest/20105.js index 517578e793..5de72b98f4 100644 --- a/scripts/quest/20105.js +++ b/scripts/quest/20105.js @@ -22,8 +22,8 @@ function end(mode, type, selection) { qm.sendYesNo("Have you made your decision? The decision will be final, so think carefully before deciding what to do. Are you sure you want to become a Thunder Breaker?"); } else if (status == 1) { if(!qm.canGetFirstJob(jobType)) { - cm.sendOk("Train a bit more and I can show you the way of the #rThunder Breaker#k."); - cm.dispose(); + qm.sendOk("Train a bit more until you reach #blevel 10, " + qm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rThunder Breaker#k."); + qm.dispose(); return; } diff --git a/scripts/quest/4647.js b/scripts/quest/4647.js index b317e99f17..c26f42341b 100644 --- a/scripts/quest/4647.js +++ b/scripts/quest/4647.js @@ -41,7 +41,7 @@ function end(mode, type, selection) { if (status == 0) { if(qm.haveItem(5460000)) { qm.sendOk("You got the Pet Snack! Thanks! You can use these to feed multiple pets at once!"); - qm.teachSkill(0008, 1, 1, -1); + qm.teachSkill(8, 1, 1, -1); qm.gainItem(5460000, -1, false); qm.completeQuest(); qm.dispose(); diff --git a/scripts/reactor/8001000.js b/scripts/reactor/8001000.js index f4f08d7db5..6be4803511 100644 --- a/scripts/reactor/8001000.js +++ b/scripts/reactor/8001000.js @@ -20,5 +20,5 @@ along with this program. If not, see . */ function act(){ - rm.spawnMonster(9400112); + rm.spawnMonster(9400112, 1, 420, 160); } \ No newline at end of file diff --git a/sql/db_database.sql b/sql/db_database.sql index 06f3974ba2..6439a3e495 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -3794,7 +3794,7 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`, (3575, 4250001, 1442005, 1, 1, 0, 700), (3576, 4250001, 4000438, 1, 1, 0, 600000), (3577, 4250001, 4130014, 1, 1, 0, 6000), -(3578, 5090000, 4000413, 1, 1, 0, 600000), +(3578, 5090000, 4000413, 1, 1, 0, 200000), (3579, 5090001, 4000412, 1, 1, 0, 600000), (3580, 5100000, 4000048, 1, 1, 0, 600000), (3581, 5100000, 4003005, 1, 1, 0, 200000), @@ -7995,7 +7995,7 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`, (7772, 8180000, 4004001, 1, 1, 0, 100000), (7773, 8180000, 4004002, 1, 1, 0, 100000), (7774, 8180000, 4004003, 1, 1, 0, 100000), -(7775, 8180000, 4000235, 1, 1, 0, 600000), +(7775, 8180000, 4000235, 1, 1, 0, 200000), (7776, 8180000, 4000244, 1, 1, 0, 20000), (7777, 8180000, 4000245, 1, 1, 0, 20000), (7778, 8180000, 2290003, 1, 1, 0, 5000), @@ -8052,7 +8052,7 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`, (7829, 8180001, 4004001, 1, 1, 0, 100000), (7830, 8180001, 4004002, 1, 1, 0, 100000), (7831, 8180001, 4004003, 1, 1, 0, 100000), -(7832, 8180001, 4000243, 1, 1, 0, 600000), +(7832, 8180001, 4000243, 1, 1, 0, 200000), (7833, 8180001, 4000244, 1, 1, 0, 20000), (7834, 8180001, 4000245, 1, 1, 0, 20000), (7835, 8180001, 2290018, 1, 1, 0, 5000), @@ -10413,10 +10413,10 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`, (10190, 9400546, 4031681, 1, 1, 4915, 1000000), (10191, 9600005, 4000191, 1, 1, 0, 1000000), (10192, 9600006, 4000192, 1, 1, 0, 1000000), -(10193, 8500002, 4031196, 1, 1, 0, 1000000), -(10194, 8500002, 4031196, 1, 1, 0, 1000000), -(10195, 8500002, 4031196, 1, 1, 0, 187500), -(10196, 8500002, 4031196, 1, 1, 0, 125000), +(10193, 8500002, 4031196, 1, 1, 0, 80000), +(10194, 8500002, 4031196, 1, 1, 0, 80000), +(10195, 8500002, 4031196, 1, 1, 0, 80000), +(10196, 8500002, 4031196, 1, 1, 0, 80000), (10197, 3000006, 4031209, 1, 1, 3072, 500000), (10198, 2230109, 4031209, 1, 1, 3072, 500000), (10199, 2230200, 4031209, 1, 1, 3072, 500000), diff --git a/sql/db_drops.sql b/sql/db_drops.sql index 3e39c0a3fe..a7cb0be4db 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -15794,15 +15794,15 @@ USE `heavenms`; (6400006, 4030012, 1, 1, 0, 125000), (6400009, 4030012, 1, 1, 0, 125000), (9303013, 4030012, 1, 1, 0, 125000), -(8150000, 4031906, 1, 1, 0, 7000), -(9300210, 4031906, 1, 1, 0, 7000), -(9500140, 4031906, 1, 1, 0, 7000), -(9500171, 4031906, 1, 1, 0, 7000), -(9500328, 4031906, 1, 1, 0, 7000), -(9500358, 4031906, 1, 1, 0, 7000), -(6400006, 4031906, 1, 1, 0, 7000), -(6400009, 4031906, 1, 1, 0, 7000), -(9303013, 4031906, 1, 1, 0, 7000), +(8150000, 4031906, 1, 1, 0, 400000), +(9300210, 4031906, 1, 1, 0, 400000), +(9500140, 4031906, 1, 1, 0, 400000), +(9500171, 4031906, 1, 1, 0, 400000), +(9500328, 4031906, 1, 1, 0, 400000), +(9500358, 4031906, 1, 1, 0, 400000), +(6400006, 4031906, 1, 1, 0, 400000), +(6400009, 4031906, 1, 1, 0, 400000), +(9303013, 4031906, 1, 1, 0, 400000), (8150000, 2043002, 1, 4, 0, 10000), (9300210, 2043002, 1, 4, 0, 10000), (9500140, 2043002, 1, 4, 0, 10000), @@ -17473,18 +17473,18 @@ USE `heavenms`; (8200010, 1072225, 1, 1, 0, 700), (8200010, 1082158, 1, 1, 0, 700), (8200010, 1002646, 1, 1, 0, 700), -(8500002, 4031196, 1, 1, 0, 7000), -(9300214, 4031196, 1, 1, 0, 7000), -(9500180, 4031196, 1, 1, 0, 7000), -(9500181, 4031196, 1, 1, 0, 7000), -(9500331, 4031196, 1, 1, 0, 7000), -(9500362, 4031196, 1, 1, 0, 7000), -(8500002, 4031901, 1, 1, 0, 7000), -(9300214, 4031901, 1, 1, 0, 7000), -(9500180, 4031901, 1, 1, 0, 7000), -(9500181, 4031901, 1, 1, 0, 7000), -(9500331, 4031901, 1, 1, 0, 7000), -(9500362, 4031901, 1, 1, 0, 7000), +(8500002, 4031196, 1, 1, 0, 80000), +(9300214, 4031196, 1, 1, 0, 80000), +(9500180, 4031196, 1, 1, 0, 80000), +(9500181, 4031196, 1, 1, 0, 80000), +(9500331, 4031196, 1, 1, 0, 80000), +(9500362, 4031196, 1, 1, 0, 80000), +(8500002, 4031901, 1, 1, 0, 400000), +(9300214, 4031901, 1, 1, 0, 400000), +(9500180, 4031901, 1, 1, 0, 400000), +(9500181, 4031901, 1, 1, 0, 400000), +(9500331, 4031901, 1, 1, 0, 400000), +(9500362, 4031901, 1, 1, 0, 400000), (8500002, 4001084, 1, 1, 0, 7000), (9300214, 4001084, 1, 1, 0, 7000), (9500180, 4001084, 1, 1, 0, 7000), @@ -20775,7 +20775,7 @@ USE `heavenms`; UPDATE drop_data SET questid=28248 WHERE itemid=4001358; UPDATE drop_data SET questid=28248 WHERE itemid=4001359; UPDATE drop_data SET questid=28175 WHERE itemid=4001342; - UPDATE drop_data SET questid=7777 WHERE itemid=4031906; #id 7777 for ALL "quest items" with no v83 quest. + UPDATE drop_data SET questid=7777 WHERE itemid=4031905; #id 7777 for ALL "quest items" with no v83 quest. UPDATE drop_data SET chance=0 WHERE itemid=2050099; UPDATE drop_data SET questid=6191 WHERE itemid=4031477; UPDATE drop_data SET questid=6190 WHERE itemid=4001111; @@ -21364,7 +21364,7 @@ USE `heavenms`; (9300270, 2022431, 1, 1, 0, 200000), (9300270, 2022432, 1, 1, 0, 200000), (9300270, 2022433, 1, 1, 0, 200000), -(6090002, 4000414, 1, 1, 0, 400000), +(6090002, 4000414, 1, 1, 0, 200000), (9300147, 4001132, 1, 1, 0, 400000), (9300148, 4001133, 1, 1, 0, 100000), (4300012, 4000537, 1, 1, 0, 200000), diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index ad3ecc6922..60ba02f2b1 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -273,10 +273,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private ScheduledFuture extraRecoveryTask = null; private ScheduledFuture chairRecoveryTask = null; private ScheduledFuture pendantOfSpirit = null; //1122017 - private Lock chrLock = new MonitoredReentrantLock(MonitoredLockType.CHR, true); - private Lock effLock = new MonitoredReentrantLock(MonitoredLockType.EFF, true); - private Lock petLock = new MonitoredReentrantLock(MonitoredLockType.PET, true); // for quest tasks as well - private Lock prtLock = new MonitoredReentrantLock(MonitoredLockType.PRT); + private Lock chrLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_CHR, true); + private Lock effLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_EFF, true); + private Lock petLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PET, true); // for quest tasks as well + private Lock prtLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PRT); private Map> excluded = new LinkedHashMap<>(); private Set excludedItems = new LinkedHashSet<>(); private static String[] ariantroomleader = new String[3]; diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index a01deed9e3..9b51e6e694 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -107,8 +107,8 @@ public class MapleClient { private byte gender = -1; private boolean disconnecting = false; private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true); - private final Lock encoderLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true); - private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true); + private final Lock encoderLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_ENCODER, true); + private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_LOGIN, true); private int votePoints; private int voteTime = -1; private long lastNpcClick; diff --git a/src/client/status/MonsterStatusEffect.java b/src/client/status/MonsterStatusEffect.java index 8cc2bc3bb1..56deeef26b 100644 --- a/src/client/status/MonsterStatusEffect.java +++ b/src/client/status/MonsterStatusEffect.java @@ -23,7 +23,6 @@ package client.status; import client.Skill; import java.util.Map; -import java.util.concurrent.ScheduledFuture; import server.life.MobSkill; import tools.ArrayMap; @@ -33,9 +32,7 @@ public class MonsterStatusEffect { private Skill skill; private MobSkill mobskill; private boolean monsterSkill; - private ScheduledFuture cancelTask; - private ScheduledFuture damageSchedule; - + public MonsterStatusEffect(Map stati, Skill skillId, MobSkill mobskill, boolean monsterSkill) { this.stati = new ArrayMap<>(stati); this.skill = skillId; @@ -59,35 +56,10 @@ public class MonsterStatusEffect { return monsterSkill; } - public final void cancelTask() { - if (cancelTask != null) { - cancelTask.cancel(false); - } - cancelTask = null; - } - - public ScheduledFuture getCancelTask() { - return cancelTask; - } - - public void setCancelTask(ScheduledFuture cancelTask) { - this.cancelTask = cancelTask; - } - public void removeActiveStatus(MonsterStatus stat) { stati.remove(stat); } - public void setDamageSchedule(ScheduledFuture damageSchedule) { - this.damageSchedule = damageSchedule; - } - - public void cancelDamageSchedule() { - if (damageSchedule != null) { - damageSchedule.cancel(false); - } - } - public MobSkill getMobSkill() { return mobskill; } diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index a76f8c4b2a..ad68592292 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -123,6 +123,10 @@ public class ServerConstants { public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map. public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions. + //Channel Mob Disease Monitor Configuration + public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule. + public static final int MOB_STATUS_MONITOR_LIFE = 84; //Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity. + //Some Gameplay Enhancing Configurations //Scroll Configuration public static final boolean USE_PERFECT_GM_SCROLL = true; //Scrolls from GMs never uses up slots nor fails. diff --git a/src/net/server/Server.java b/src/net/server/Server.java index 8b7a724977..c7c70e273c 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -96,8 +96,8 @@ public class Server { private final Map guilds = new HashMap<>(100); private final Map inLoginState = new HashMap<>(100); private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER); - private final Lock lgnLock = new MonitoredReentrantLock(MonitoredLockType.SERVER); - private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER); + private final Lock lgnLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_LOGIN); + private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_DISEASES); private final PlayerBuffStorage buffStorage = new PlayerBuffStorage(); private final Map alliances = new HashMap<>(100); private final Map newyears = new HashMap<>(); diff --git a/src/net/server/audit/ThreadTracker.java b/src/net/server/audit/ThreadTracker.java index 50e0d49f1a..9d3ed7dd3c 100644 --- a/src/net/server/audit/ThreadTracker.java +++ b/src/net/server/audit/ThreadTracker.java @@ -233,7 +233,7 @@ public class ThreadTracker { List list = threadTracker.get(tid); for(int i = list.size() - 1; i >= 0; i--) { - if(lockId.getValue() == list.get(i).getValue()) { + if(lockId.equals(list.get(i))) { list.remove(i); break; } diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java index b4c3b45499..ad43f71bd6 100644 --- a/src/net/server/channel/Channel.java +++ b/src/net/server/channel/Channel.java @@ -21,6 +21,7 @@ along with this program. If not, see . */ package net.server.channel; +import net.server.channel.worker.MobStatusScheduler; import java.io.File; import java.net.InetSocketAddress; import java.util.ArrayList; @@ -70,6 +71,7 @@ import server.maps.MapleMiniDungeon; import tools.MaplePacketCreator; import tools.Pair; import client.MapleCharacter; +import client.status.MonsterStatusEffect; import constants.ServerConstants; import server.maps.MapleMiniDungeonInfo; import tools.locks.MonitoredLockType; @@ -83,6 +85,7 @@ public final class Channel { private String ip, serverMessage; private MapleMapFactory mapFactory; private EventScriptManager eventSM; + private MobStatusScheduler mobStatusSchedulers[] = new MobStatusScheduler[4]; private Map hiredMerchants = new HashMap<>(); private final Map storedVars = new HashMap<>(); private List expeditions = new ArrayList<>(); @@ -149,6 +152,10 @@ public final class Channel { dojoTask[i] = null; } + for(int i = 0; i < 4; i++) { + mobStatusSchedulers[i] = new MobStatusScheduler(); + } + System.out.println(" Channel " + getId() + ": Listening on port " + port); } catch (Exception e) { e.printStackTrace(); @@ -817,6 +824,34 @@ public final class Channel { } } + private static int getMobStatusSchedulerIndex(int mapid) { + if(mapid >= 250000000) { + if(mapid >= 900000000) { + return 3; + } else { + return 2; + } + } else { + if(mapid >= 200000000) { + return 1; + } else { + return 0; + } + } + } + + public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration) { + registerMobStatus(mapid, mse, cancelAction, duration, null, -1); + } + + public void registerMobStatus(int mapid, MonsterStatusEffect mse, Runnable cancelAction, long duration, Runnable overtimeAction, int overtimeDelay) { + mobStatusSchedulers[getMobStatusSchedulerIndex(mapid)].registerMobStatus(mse, cancelAction, duration, overtimeAction, overtimeDelay); + } + + public void interruptMobStatus(int mapid, MonsterStatusEffect mse) { + mobStatusSchedulers[getMobStatusSchedulerIndex(mapid)].interruptMobStatus(mse); + } + public void debugMarriageStatus() { System.out.println(" ----- WORLD DATA -----"); Server.getInstance().getWorld(world).debugMarriageStatus(); diff --git a/src/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/net/server/channel/handlers/AbstractDealDamageHandler.java index c123a49695..aec0ca92f1 100644 --- a/src/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -326,6 +326,15 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } else if (attack.skill == Outlaw.HOMING_BEACON || attack.skill == Corsair.BULLSEYE) { player.setMarkedMonster(monster.getObjectId()); player.announce(MaplePacketCreator.giveBuff(1, attack.skill, Collections.singletonList(new Pair<>(MapleBuffStat.HOMING_BEACON, monster.getObjectId())))); + } else if (attack.skill == Outlaw.FLAME_THROWER) { + if (!monster.isBoss()) { + Skill type = SkillFactory.getSkill(Outlaw.FLAME_THROWER); + if (player.getSkillLevel(type) > 0) { + MapleStatEffect DoT = type.getEffect(player.getSkillLevel(type)); + MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.POISON, 1), type, null, false); + monster.applyStatus(player, monsterStatusEffect, true, DoT.getDuration(), false); + } + } } if (job == 2111 || job == 2112) { @@ -406,15 +415,6 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl } } } - } else if (job == 521 || job == 522) { // from what I can gather this is how it should work - if (!monster.isBoss()) { - Skill type = SkillFactory.getSkill(Outlaw.FLAME_THROWER); - if (player.getSkillLevel(type) > 0) { - MapleStatEffect DoT = type.getEffect(player.getSkillLevel(type)); - MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(Collections.singletonMap(MonsterStatus.POISON, 1), type, null, false); - monster.applyStatus(player, monsterStatusEffect, true, DoT.getDuration(), false); - } - } } else if (job >= 311 && job <= 322) { if (!monster.isBoss()) { Skill mortalBlow; diff --git a/src/net/server/channel/handlers/MoveLifeHandler.java b/src/net/server/channel/handlers/MoveLifeHandler.java index b79c423aea..6223b19f31 100644 --- a/src/net/server/channel/handlers/MoveLifeHandler.java +++ b/src/net/server/channel/handlers/MoveLifeHandler.java @@ -27,6 +27,7 @@ import java.awt.Point; import java.util.ArrayList; import java.util.List; import server.life.MapleMonster; +import server.life.MapleMonsterInformationProvider; //import server.life.MobAttackInfo; //import server.life.MobAttackInfoFactory; import server.life.MobSkill; @@ -94,20 +95,27 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler { } else if (toUse.getHP() < percHpLeft) { toUse = null; } else if (monster.canUseSkill(toUse)) { - toUse.applyEffect(c.getPlayer(), monster, true, banishPlayers); + int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(monster.getId(), Random); + if(animationTime > 0) { + toUse.applyDelayedEffect(c.getPlayer(), monster, true, banishPlayers, animationTime); + } else { + toUse.applyEffect(c.getPlayer(), monster, true, banishPlayers); + } } else { toUse = null; } } else { + toUse = null; // paliative measure for suspicious mob movement + + /* long curtime = System.currentTimeMillis(); if(curtime >= monster.getNextBasicSkillTime()) { // dont use the special attack too often, chase the player f3 - //MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId); - //monster.setNextBasicSkillTime(curtime); - - toUse = null; // paliative measure for suspicious mob movement + MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId); + monster.setNextBasicSkillTime(curtime); } else { toUse = null; } + */ } } diff --git a/src/net/server/channel/worker/MobStatusScheduler.java b/src/net/server/channel/worker/MobStatusScheduler.java new file mode 100644 index 0000000000..083ade0827 --- /dev/null +++ b/src/net/server/channel/worker/MobStatusScheduler.java @@ -0,0 +1,148 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft 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.worker; + +import client.status.MonsterStatusEffect; +import constants.ServerConstants; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.locks.Lock; +import server.TimerManager; +import tools.Pair; +import tools.locks.MonitoredLockType; +import tools.locks.MonitoredReentrantLock; + +/** + * + * @author Ronan + */ +public class MobStatusScheduler { + private int idleProcs = 0; + private Map> registeredMobStatus = new HashMap<>(); + private Map registeredMobStatusOvertime = new HashMap<>(); + + private class MobStatusOvertimeEntry { + private int procCount; + private int procLimit; + private Runnable r; + + protected MobStatusOvertimeEntry(int delay, Runnable run) { + procCount = 0; + procLimit = (int)Math.ceil((float) delay / ServerConstants.MOB_STATUS_MONITOR_PROC); + r = run; + } + + protected void update() { + procCount++; + if(procCount >= procLimit) { + procCount = 0; + r.run(); + } + } + } + + private ScheduledFuture mobStatusSchedule = null; + private Lock mobStatusLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_MOBSTATUS, true); + private Runnable monitorTask = new Runnable() { + @Override + public void run() { + runMobStatusSchedule(); + } + }; + + private void runMobStatusSchedule() { + mobStatusLock.lock(); + try { + if(registeredMobStatus.isEmpty()) { + idleProcs++; + + if(idleProcs >= ServerConstants.MOB_STATUS_MONITOR_LIFE) { + if(mobStatusSchedule != null) { + mobStatusSchedule.cancel(false); + mobStatusSchedule = null; + } + } + + return; + } + idleProcs = 0; + + long timeNow = System.currentTimeMillis(); + List toRemove = new LinkedList<>(); + for(Entry> rmd : registeredMobStatus.entrySet()) { + Pair r = rmd.getValue(); + + if(r.getRight() < timeNow) { + r.getLeft().run(); // runs the cancel action + toRemove.add(rmd.getKey()); + } + } + + for(MonsterStatusEffect mse : toRemove) { + registeredMobStatus.remove(mse); + registeredMobStatusOvertime.remove(mse); + } + + // it's probably ok to use one thread for both management & overtime actions + List mdoeList = new ArrayList<>(registeredMobStatusOvertime.values()); + for(MobStatusOvertimeEntry mdoe : mdoeList) { + mdoe.update(); + } + } finally { + mobStatusLock.unlock(); + } + } + + public void registerMobStatus(MonsterStatusEffect mse, Runnable cancelStatus, long duration, Runnable overtimeStatus, int overtimeDelay) { + mobStatusLock.lock(); + try { + idleProcs = 0; + if(mobStatusSchedule == null) { + mobStatusSchedule = TimerManager.getInstance().register(monitorTask, ServerConstants.MOB_STATUS_MONITOR_PROC, ServerConstants.MOB_STATUS_MONITOR_PROC); + } + + registeredMobStatus.put(mse, new Pair<>(cancelStatus, System.currentTimeMillis() + duration)); + + if(overtimeStatus != null) { + MobStatusOvertimeEntry mdoe = new MobStatusOvertimeEntry(overtimeDelay, overtimeStatus); + registeredMobStatusOvertime.put(mse, mdoe); + } + } finally { + mobStatusLock.unlock(); + } + } + + public void interruptMobStatus(MonsterStatusEffect mse) { + mobStatusLock.lock(); + try { + Pair rmd = registeredMobStatus.remove(mse); + if(rmd != null) rmd.getLeft().run(); + + registeredMobStatusOvertime.remove(mse); + } finally { + mobStatusLock.unlock(); + } + } +} diff --git a/src/net/server/worker/BaseWorker.java b/src/net/server/worker/BaseWorker.java index 283415f478..1b40005331 100644 --- a/src/net/server/worker/BaseWorker.java +++ b/src/net/server/worker/BaseWorker.java @@ -24,7 +24,7 @@ import net.server.world.World; /** * @author Ronan */ -public class BaseWorker implements Runnable { +public abstract class BaseWorker implements Runnable { protected World wserv; @Override diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index c2eae5bcf9..155f2366f7 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -124,7 +124,7 @@ public class World { private Map registeredTimedMapObjects = new LinkedHashMap<>(); private ScheduledFuture timedMapObjectsSchedule; - private Lock timedMapObjectLock = new MonitoredReentrantLock(MonitoredLockType.MAP_OBJS, true); + private Lock timedMapObjectLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MAPOBJS, true); private ScheduledFuture charactersSchedule; private ScheduledFuture marriagesSchedule; diff --git a/src/scripting/AbstractPlayerInteraction.java b/src/scripting/AbstractPlayerInteraction.java index 6db9031a94..1f5e5e8358 100644 --- a/src/scripting/AbstractPlayerInteraction.java +++ b/src/scripting/AbstractPlayerInteraction.java @@ -978,4 +978,23 @@ public class AbstractPlayerInteraction { return true; } } + + public static String getFirstJobStatRequirement(int jobType) { + switch(jobType) { + case 1: + return "STR " + 35; + + case 2: + return "INT " + 20; + + case 3: + case 4: + return "DEX " + 25; + + case 5: + return "DEX " + 20; + } + + return null; + } } diff --git a/src/scripting/AbstractScriptManager.java b/src/scripting/AbstractScriptManager.java index 02c741ed5a..2ec8782b11 100644 --- a/src/scripting/AbstractScriptManager.java +++ b/src/scripting/AbstractScriptManager.java @@ -69,7 +69,7 @@ public abstract class AbstractScriptManager { } try (FileReader fr = new FileReader(scriptFile)) { if (ServerConstants.JAVA_8){ - engine.eval("load('nashorn:mozilla_compat.js');"); + engine.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); } engine.eval(fr); } catch (final ScriptException | IOException t) { diff --git a/src/scripting/item/ItemScriptManager.java b/src/scripting/item/ItemScriptManager.java index 33319d47b9..b5d11eade1 100644 --- a/src/scripting/item/ItemScriptManager.java +++ b/src/scripting/item/ItemScriptManager.java @@ -22,6 +22,7 @@ along with this program. If not, see . package scripting.item; import client.MapleClient; +import constants.ServerConstants; import java.io.File; import java.io.FileReader; import java.io.IOException; @@ -29,7 +30,6 @@ import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.Map; import javax.script.Compilable; -import javax.script.CompiledScript; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; @@ -76,8 +76,13 @@ public class ItemScriptManager { ScriptEngine portal = sef.getScriptEngine(); try { fr = new FileReader(scriptFile); - CompiledScript compiled = ((Compilable) portal).compile(fr); - compiled.eval(); + + // java 8 support here thanks to Arufonsu + if (ServerConstants.JAVA_8){ + portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); + } + + ((Compilable) portal).compile(fr).eval(); final Invocable script = ((Invocable) portal); scripts.put(scriptName, script); diff --git a/src/scripting/map/MapScriptManager.java b/src/scripting/map/MapScriptManager.java index a8ad9d45b6..303f1ae1fc 100644 --- a/src/scripting/map/MapScriptManager.java +++ b/src/scripting/map/MapScriptManager.java @@ -22,6 +22,7 @@ along with this program. If not, see . package scripting.map; import client.MapleClient; +import constants.ServerConstants; import java.io.File; import java.io.FileReader; import java.io.IOException; @@ -29,7 +30,6 @@ import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.Map; import javax.script.Compilable; -import javax.script.CompiledScript; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; @@ -80,8 +80,14 @@ public class MapScriptManager { ScriptEngine portal = sef.getScriptEngine(); try { fr = new FileReader(scriptFile); - CompiledScript compiled = ((Compilable) portal).compile(fr); - compiled.eval(); + + // java 8 support here thanks to Arufonsu + if (ServerConstants.JAVA_8){ + portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); + } + + ((Compilable) portal).compile(fr).eval(); + final Invocable script = ((Invocable) portal); scripts.put(scriptName, script); script.invokeFunction("start", new MapScriptMethods(c)); diff --git a/src/scripting/portal/PortalScriptManager.java b/src/scripting/portal/PortalScriptManager.java index 8875f32a15..fd85fe5cc3 100644 --- a/src/scripting/portal/PortalScriptManager.java +++ b/src/scripting/portal/PortalScriptManager.java @@ -22,6 +22,7 @@ along with this program. If not, see . package scripting.portal; import client.MapleClient; +import constants.ServerConstants; import java.io.File; import java.io.FileReader; import java.io.IOException; @@ -65,6 +66,12 @@ public class PortalScriptManager { ScriptEngine portal = sef.getScriptEngine(); try { fr = new FileReader(scriptFile); + + // java 8 support here thanks to Arufonsu + if (ServerConstants.JAVA_8){ + portal.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator()); + } + ((Compilable) portal).compile(fr).eval(); } catch (ScriptException | IOException | UndeclaredThrowableException e) { FilePrinter.printError(FilePrinter.PORTAL + scriptName + ".txt", e); diff --git a/src/server/life/MapleLifeFactory.java b/src/server/life/MapleLifeFactory.java index b39005682f..5d5f3655d3 100644 --- a/src/server/life/MapleLifeFactory.java +++ b/src/server/life/MapleLifeFactory.java @@ -132,12 +132,23 @@ public class MapleLifeFactory { stats.setRevives(revives); } decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, "")); - MapleData monsterSkillData = monsterInfoData.getChildByPath("skill"); - if (monsterSkillData != null) { + MapleData monsterSkillInfoData = monsterInfoData.getChildByPath("skill"); + if (monsterSkillInfoData != null) { int i = 0; List> skills = new ArrayList<>(); - while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) { - skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0)))); + while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) { + skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillInfoData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillInfoData, 0)))); + + MapleData monsterSkillData = monsterData.getChildByPath("skill" + i); + if(monsterSkillData != null) { + int animationTime = 0; + for(MapleData effectEntry : monsterSkillData.getChildren()) { + animationTime += MapleDataTool.getIntConvert("delay", effectEntry, 0); + } + + MapleMonsterInformationProvider.getInstance().setMobSkillAnimationTime(mid, i, animationTime); + } + i++; } stats.setSkills(skills); diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index fe9449b7f0..c7d3d30a72 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -56,6 +56,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import tools.locks.MonitoredReentrantLock; +import net.server.channel.Channel; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; import server.TimerManager; @@ -320,7 +321,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { return takenDamage.containsKey(chr.getId()); } - private void distributeExperienceToParty(int pid, int exp, int killer, Set underleveled, int minThresholdLevel) { + private void distributeExperienceToParty(int pid, float exp, int killer, Set underleveled, int minThresholdLevel) { List members = new LinkedList<>(); MapleCharacter pchar = getMap().getAnyCharacterFromParty(pid); if(pchar != null) { @@ -353,7 +354,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (level >= minThresholdLevel) { boolean isKiller = killer == id; boolean mostDamage = mostDamageCid == id; - int xp = (int) ((0.80f * exp * level) / partyLevel); + float xp = ((0.80f * exp * level) / partyLevel); if (mostDamage) { xp += (0.20f * exp); } @@ -378,14 +379,14 @@ public class MapleMonster extends AbstractLoadedMapleLife { int minThresholdLevel = calcThresholdLevel(this.getMap().getEventInstance() != null); int exp = getExp(); long totalHealth = maxHpPlusHeal.get(); - Map expDist = new HashMap<>(); - Map partyExp = new HashMap<>(); + Map expDist = new HashMap<>(); + Map partyExp = new HashMap<>(); float exp8perHp = (0.8f * exp) / totalHealth; // 80% of pool is split amongst all the damagers float exp2 = (0.2f * exp); // 20% of pool goes to the killer or his/her party for (Entry damage : takenDamage.entrySet()) { - expDist.put(damage.getKey(), (int) (Math.min((exp8perHp * damage.getValue().get()), Integer.MAX_VALUE))); + expDist.put(damage.getKey(), exp8perHp * damage.getValue().get()); } Collection chrs = map.getCharacters(); @@ -393,15 +394,16 @@ public class MapleMonster extends AbstractLoadedMapleLife { for (MapleCharacter mc : chrs) { if (expDist.containsKey(mc.getId())) { boolean isKiller = (mc.getId() == killerId); - int xp = expDist.get(mc.getId()); + float xp = expDist.get(mc.getId()); if (isKiller) { - xp = (int)Math.min(exp2 + xp, Integer.MAX_VALUE); + xp += exp2; } + MapleParty p = mc.getParty(); if (p != null) { int pID = p.getId(); - long pXP = (long) xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0); - partyExp.put(pID, (int)Math.min(pXP, Integer.MAX_VALUE)); + float pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0); + partyExp.put(pID, pXP); } else { if(mc.getLevel() >= minThresholdLevel) { //NO EXP WILL BE GIVEN for those who are underleveled! @@ -413,7 +415,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } - for (Entry party : partyExp.entrySet()) { + for (Entry party : partyExp.entrySet()) { distributeExperienceToParty(party.getKey(), party.getValue(), killerId, underleveled, minThresholdLevel); } @@ -422,7 +424,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } - public void giveExpToCharacter(MapleCharacter attacker, int exp, boolean isKiller, int numExpSharers) { + public void giveExpToCharacter(MapleCharacter attacker, float exp, boolean isKiller, int numExpSharers) { if (isKiller) { if (getMap().getEventInstance() != null) { getMap().getEventInstance().monsterKilled(attacker, this); @@ -434,9 +436,10 @@ public class MapleMonster extends AbstractLoadedMapleLife { int partyExp = 0; if (attacker.getHp() > 0) { - int personalExp = exp * attacker.getExpRate(); + exp *= attacker.getExpRate(); + int personalExp = (int) exp; - if (exp > 0) { + if (exp <= Integer.MAX_VALUE) { // assuming no negative xp here if (partyModifier > 0.0f) { partyExp = (int) (personalExp * partyModifier * ServerConstants.PARTY_BONUS_EXP_RATE); } @@ -444,7 +447,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (holySymbol != null) { personalExp *= 1.0 + (holySymbol.doubleValue() / 100.0); } - + statiLock.lock(); try { if (stati.containsKey(MonsterStatus.SHOWDOWN)) { @@ -453,6 +456,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { } finally { statiLock.unlock(); } + } else { + personalExp = Integer.MAX_VALUE; } attacker.gainExp(personalExp, partyExp, true, false, isKiller); @@ -777,9 +782,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), status, null); map.broadcastMessage(packet, getPosition()); - MapleCharacter controller = getController(); - if (controller != null && !controller.isMapObjectVisible(this)) { - controller.getClient().announce(packet); + MapleCharacter chrController = getController(); + if (chrController != null && !chrController.isMapObjectVisible(this)) { + chrController.getClient().announce(packet); } return animationTime; @@ -832,6 +837,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } + final Channel ch = map.getChannelServer(); + final int mapid = map.getId(); if(statis.size() > 0) { statiLock.lock(); try { @@ -840,8 +847,7 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (oldEffect != null) { oldEffect.removeActiveStatus(stat); if (oldEffect.getStati().isEmpty()) { - oldEffect.cancelTask(); - oldEffect.cancelDamageSchedule(); + ch.interruptMobStatus(mapid, oldEffect); } } } @@ -850,7 +856,6 @@ public class MapleMonster extends AbstractLoadedMapleLife { } } - TimerManager timerManager = TimerManager.getInstance(); final Runnable cancelTask = new Runnable() { @Override @@ -875,17 +880,21 @@ public class MapleMonster extends AbstractLoadedMapleLife { } setVenomMulti(0); - status.cancelDamageSchedule(); } }; + Runnable overtimeAction = null; + int overtimeDelay = -1; + int animationTime; if (poison) { int poisonLevel = from.getSkillLevel(status.getSkill()); int poisonDamage = Math.min(Short.MAX_VALUE, (int) (getMaxHp() / (70.0 - poisonLevel) + 0.999)); status.setValue(MonsterStatus.POISON, Integer.valueOf(poisonDamage)); animationTime = broadcastStatusEffect(status); - status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000)); + + overtimeAction = new DamageTask(poisonDamage, from, status, 0); + overtimeDelay = 1000; } else if (venom) { if (from.getJob() == MapleJob.NIGHTLORD || from.getJob() == MapleJob.SHADOWER || from.getJob().isA(MapleJob.NIGHTWALKER3)) { int poisonLevel, matk, jobid = from.getJob().getId(); @@ -910,7 +919,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { status.setValue(MonsterStatus.VENOMOUS_WEAPON, Integer.valueOf(poisonDamage)); status.setValue(MonsterStatus.POISON, Integer.valueOf(poisonDamage)); animationTime = broadcastStatusEffect(status); - status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000)); + + overtimeAction = new DamageTask(poisonDamage, from, status, 0); + overtimeDelay = 1000; } else { return false; } @@ -919,7 +930,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { int webDamage = (int) (getMaxHp() / 50.0 + 0.999); status.setValue(MonsterStatus.SHADOW_WEB, Integer.valueOf(webDamage)); animationTime = broadcastStatusEffect(status); - status.setDamageSchedule(timerManager.schedule(new DamageTask(webDamage, from, status, cancelTask, 1), 3500)); + + overtimeAction = new DamageTask(webDamage, from, status, 1); + overtimeDelay = 3500; */ } else if (status.getSkill().getId() == 4121004 || status.getSkill().getId() == 4221004) { // Ninja Ambush final Skill skill = SkillFactory.getSkill(status.getSkill().getId()); @@ -928,7 +941,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { status.setValue(MonsterStatus.NINJA_AMBUSH, Integer.valueOf(damage)); animationTime = broadcastStatusEffect(status); - status.setDamageSchedule(timerManager.register(new DamageTask(damage, from, status, cancelTask, 2), 1000, 1000)); + + overtimeAction = new DamageTask(damage, from, status, 2); + overtimeDelay = 1000; } else { animationTime = broadcastStatusEffect(status); } @@ -943,12 +958,11 @@ public class MapleMonster extends AbstractLoadedMapleLife { statiLock.unlock(); } - status.setCancelTask(timerManager.schedule(cancelTask, duration + animationTime - 100)); + ch.registerMobStatus(mapid, status, cancelTask, duration + animationTime - 100, overtimeAction, overtimeDelay); return true; } public void applyMonsterBuff(final Map stats, final int x, int skillId, long duration, MobSkill skill, final List reflection) { - TimerManager timerManager = TimerManager.getInstance(); final Runnable cancelTask = new Runnable() { @Override @@ -991,7 +1005,8 @@ public class MapleMonster extends AbstractLoadedMapleLife { if (controller != null && !controller.isMapObjectVisible(this)) { controller.getClient().announce(packet); } - effect.setCancelTask(timerManager.schedule(cancelTask, duration)); + + map.getChannelServer().registerMobStatus(map.getId(), effect, cancelTask, duration); } private void debuffMobStat(MonsterStatus stat) { @@ -1191,15 +1206,13 @@ public class MapleMonster extends AbstractLoadedMapleLife { private final int dealDamage; private final MapleCharacter chr; private final MonsterStatusEffect status; - private final Runnable cancelTask; private final int type; private final MapleMap map; - private DamageTask(int dealDamage, MapleCharacter chr, MonsterStatusEffect status, Runnable cancelTask, int type) { + private DamageTask(int dealDamage, MapleCharacter chr, MonsterStatusEffect status, int type) { this.dealDamage = dealDamage; this.chr = chr; this.status = status; - this.cancelTask = cancelTask; this.type = type; this.map = chr.getMap(); } @@ -1207,20 +1220,26 @@ public class MapleMonster extends AbstractLoadedMapleLife { @Override public void run() { int curHp = hp.get(); - if(curHp <= 1) return; + if(curHp <= 1) { + map.getChannelServer().interruptMobStatus(map.getId(), status); + return; + } int damage = dealDamage; if (damage >= curHp) { damage = curHp - 1; if (type == 1 || type == 2) { - cancelTask.run(); - status.getCancelTask().cancel(false); + map.getChannelServer().interruptMobStatus(map.getId(), status); } } if (damage > 0) { damage(chr, damage, true); - if (type == 1) { // ninja ambush (type 2) is already displaying DOT + if (type == 1) { map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition()); + } else if (type == 2) { + if(damage < dealDamage) { // ninja ambush (type 2) is already displaying DOT to the caster + map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition()); + } } } } diff --git a/src/server/life/MapleMonsterInformationProvider.java b/src/server/life/MapleMonsterInformationProvider.java index b24a3679f4..41eaa84bce 100644 --- a/src/server/life/MapleMonsterInformationProvider.java +++ b/src/server/life/MapleMonsterInformationProvider.java @@ -53,6 +53,8 @@ public class MapleMonsterInformationProvider { private final Map> dropsChancePool = new HashMap<>(); // thanks to ronan private final Set hasNoMultiEquipDrops = new HashSet<>(); private final Map> extraMultiEquipDrops = new HashMap<>(); + + private final Map, Integer> mobSkillAnimationTime = new HashMap<>(); protected MapleMonsterInformationProvider() { retrieveGlobal(); @@ -223,9 +225,17 @@ public class MapleMonsterInformationProvider { dropsChancePool.put(monsterId, ret); return ret; } + + public final void setMobSkillAnimationTime(int monsterId, int skillPos, int animationTime) { + mobSkillAnimationTime.put(new Pair<>(monsterId, skillPos), animationTime); + } + + public final Integer getMobSkillAnimationTime(int monsterId, int skillPos) { + Integer time = mobSkillAnimationTime.get(new Pair<>(monsterId, skillPos)); + return time == null ? 0 : time; + } - public static ArrayList> getMobsIDsFromName(String search) - { + public static ArrayList> getMobsIDsFromName(String search) { MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz")); ArrayList> retMobs = new ArrayList>(); MapleData data = dataProvider.getData("Mob.img"); @@ -243,8 +253,7 @@ public class MapleMonsterInformationProvider { return retMobs; } - public static String getMobNameFromId(int id) - { + public static String getMobNameFromId(int id) { try { return MapleLifeFactory.getMonster(id).getName(); @@ -261,8 +270,7 @@ public class MapleMonsterInformationProvider { } } - public static String getMobNameFromID(int id) - { + public static String getMobNameFromID(int id) { try { return MapleLifeFactory.getMonster(id).getName(); diff --git a/src/server/life/MobSkill.java b/src/server/life/MobSkill.java index 9ea11cd784..032b6ab662 100644 --- a/src/server/life/MobSkill.java +++ b/src/server/life/MobSkill.java @@ -32,6 +32,7 @@ import client.status.MonsterStatus; import constants.GameConstants; import java.util.LinkedList; import java.util.Map; +import server.TimerManager; import tools.Randomizer; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; @@ -103,6 +104,17 @@ public class MobSkill { this.limit = limit; } + public void applyDelayedEffect(final MapleCharacter player, final MapleMonster monster, final boolean skill, final List banishPlayers, int animationTime) { + TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + if(monster.isAlive()) { + applyEffect(player, monster, skill, banishPlayers); + } + } + }, animationTime); + } + public void applyEffect(MapleCharacter player, MapleMonster monster, boolean skill, List banishPlayers) { MapleDisease disease = null; Map stats = new ArrayMap(); @@ -282,15 +294,15 @@ public class MobSkill { System.out.println("Unhandled Mob skill: " + skillId); break; } - if (stats.size() > 0) { - if (lt != null && rb != null && skill) { - for (MapleMapObject mons : getObjectsInRange(monster, MapleMapObjectType.MONSTER)) { - ((MapleMonster) mons).applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection); - } - } else { - monster.applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection); - } - } + if (stats.size() > 0) { + if (lt != null && rb != null && skill) { + for (MapleMapObject mons : getObjectsInRange(monster, MapleMapObjectType.MONSTER)) { + ((MapleMonster) mons).applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection); + } + } else { + monster.applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection); + } + } if (disease != null) { if (lt != null && rb != null && skill) { int i = 0; diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 4e37f71fcd..c9fcd86f71 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -59,7 +59,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.lang.ref.WeakReference; -import java.util.Comparator; import net.server.Server; import net.server.channel.Channel; import scripting.map.MapScriptManager; @@ -255,15 +254,43 @@ public class MapleMap { public int getId() { return mapid; } + + public Channel getChannelServer() { + return Server.getInstance().getWorld(world).getChannel(channel); + } public MapleMap getReturnMap() { if(returnMapId == 999999999) return this; - return Server.getInstance().getWorld(world).getChannel(channel).getMapFactory().getMap(returnMapId); + return getChannelServer().getMapFactory().getMap(returnMapId); } public int getReturnMapId() { return returnMapId; } + + public MapleMap getForcedReturnMap() { + return getChannelServer().getMapFactory().getMap(forcedReturnMap); + } + + public int getForcedReturnId() { + return forcedReturnMap; + } + + public void setForcedReturnMap(int map) { + this.forcedReturnMap = map; + } + + public long getTimeLimit() { + return timeLimit; + } + + public void setTimeLimit(int timeLimit) { + this.timeLimit = timeLimit; + } + + public int getTimeLeft() { + return (int) ((timeLimit - System.currentTimeMillis()) / 1000); + } public void setReactorState() { chrRLock.lock(); @@ -322,30 +349,6 @@ public class MapleMap { return true; } - public int getForcedReturnId() { - return forcedReturnMap; - } - - public MapleMap getForcedReturnMap() { - return Server.getInstance().getWorld(world).getChannel(channel).getMapFactory().getMap(forcedReturnMap); - } - - public void setForcedReturnMap(int map) { - this.forcedReturnMap = map; - } - - public long getTimeLimit() { - return timeLimit; - } - - public void setTimeLimit(int timeLimit) { - this.timeLimit = timeLimit; - } - - public int getTimeLeft() { - return (int) ((timeLimit - System.currentTimeMillis()) / 1000); - } - public int getCurrentPartyId() { for (MapleCharacter chr : this.getCharacters()) { if (chr.getPartyId() != -1) { diff --git a/src/server/maps/MapleReactor.java b/src/server/maps/MapleReactor.java index 56fd2cd229..5d3e398e78 100644 --- a/src/server/maps/MapleReactor.java +++ b/src/server/maps/MapleReactor.java @@ -55,7 +55,7 @@ public class MapleReactor extends AbstractMapleMapObject { private boolean attackHit; private ScheduledFuture timeoutTask = null; private Lock reactorLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR, true); - private Lock hitLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR, true); + private Lock hitLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR_HIT, true); public MapleReactor(MapleReactorStats stats, int rid) { this.evstate = (byte)0; diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 7db90e26e8..ca12f0e55b 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -3877,12 +3877,13 @@ public class MaplePacketCreator { } public static byte[] applyMonsterStatus(final int oid, final MonsterStatusEffect mse, final List reflection) { + Map stati = mse.getStati(); final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.APPLY_MONSTER_STATUS.getValue()); mplew.writeInt(oid); mplew.writeLong(0); - writeIntMask(mplew, mse.getStati()); - for (Map.Entry stat : mse.getStati().entrySet()) { + writeIntMask(mplew, stati); + for (Map.Entry stat : stati.entrySet()) { mplew.writeShort(stat.getValue()); if (mse.isMonsterSkill()) { mplew.writeShort(mse.getMobSkill().getSkillId()); @@ -3892,7 +3893,7 @@ public class MaplePacketCreator { } mplew.writeShort(-1); // might actually be the buffTime but it's not displayed anywhere } - int size = mse.getStati().size(); // size + int size = stati.size(); // size if (reflection != null) { for (Integer ref : reflection) { mplew.writeInt(ref); diff --git a/src/tools/locks/MonitoredLockType.java b/src/tools/locks/MonitoredLockType.java index fee7c5ca6e..7930122041 100644 --- a/src/tools/locks/MonitoredLockType.java +++ b/src/tools/locks/MonitoredLockType.java @@ -1,6 +1,6 @@ /* This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2018 RonanLana + Copyleft 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 @@ -25,59 +25,56 @@ package tools.locks; */ public enum MonitoredLockType { - UNDEFINED(-1), - CHR(0), - EFF(1), - PET(2), - PRT(3), - CLIENT(4), - BOOK(5), - ITEM(6), - INVENTORY(7), - SRVHANDLER_IDLE(8), - SRVHANDLER_TEMP(9), - BUFF_STORAGE(10), - PLAYER_STORAGE(11), - SERVER(12), - MERCHANT(13), - CHANNEL(14), - GUILD(15), - PARTY(16), - WORLD_PARTY(17), - WORLD_OWL(18), - WORLD_PETS(19), - WORLD_MOUNTS(20), - WORLD_PSHOPS(21), - WORLD_MERCHS(21), - EIM(22), - EIM_PARTY(23), - EIM_SCRIPT(24), - EM_LOBBY(25), - EM_QUEUE(26), - CASHSHOP(27), - VISITOR_PSHOP(28), - STORAGE(29), - MOB_EXT(30), - MOB(31), - MOB_STATI(32), - MOBSKILL_FACTORY(33), - PORTAL(34), - VISITOR_MERCH(35), - MAP_CHRS(36), - MAP_OBJS(37), - MAP_FACTORY(38), - MAP_ITEM(39), - MAP_BOUNDS(40), - MINIDUNGEON(41), - REACTOR(42); - - private final int i; - - private MonitoredLockType(int val) { - this.i = val; - } - - public int getValue() { - return i; - } + UNDEFINED, + CHARACTER_CHR, + CHARACTER_EFF, + CHARACTER_PET, + CHARACTER_PRT, + CLIENT, + CLIENT_ENCODER, + CLIENT_LOGIN, + BOOK, + ITEM, + INVENTORY, + SRVHANDLER_IDLE, + SRVHANDLER_TEMP, + BUFF_STORAGE, + PLAYER_STORAGE, + SERVER, + SERVER_LOGIN, + SERVER_DISEASES, + MERCHANT, + CHANNEL, + CHANNEL_MOBSTATUS, + GUILD, + PARTY, + WORLD_PARTY, + WORLD_OWL, + WORLD_PETS, + WORLD_MOUNTS, + WORLD_PSHOPS, + WORLD_MERCHS, + WORLD_MAPOBJS, + EIM, + EIM_PARTY, + EIM_SCRIPT, + EM_LOBBY, + EM_QUEUE, + CASHSHOP, + VISITOR_PSHOP, + STORAGE, + MOB_EXT, + MOB, + MOB_STATI, + MOBSKILL_FACTORY, + PORTAL, + VISITOR_MERCH, + MAP_CHRS, + MAP_OBJS, + MAP_FACTORY, + MAP_ITEM, + MAP_BOUNDS, + MINIDUNGEON, + REACTOR, + REACTOR_HIT; } diff --git a/tools/MapleQuestItemFetcher/dist/MapleQuestItemFetcher.jar b/tools/MapleQuestItemFetcher/dist/MapleQuestItemFetcher.jar index a81e6a6878f94b4cf9775f3968ea6efa8a4de691..c16e02e882d46604013fb7af56b6bf3f3aa7e4f9 100644 GIT binary patch delta 10014 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 9330 EXPIRED diff --git a/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java b/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java index 47e1c9ad84..8302ee5a39 100644 --- a/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java +++ b/tools/MapleQuestItemFetcher/src/maplequestitemfetcher/MapleQuestItemFetcher.java @@ -403,36 +403,29 @@ public class MapleQuestItemFetcher { } } - private static boolean foundMatchingDataOnFile(Scanner scan, String searchStr) { - while(scan.hasNext()){ - String line = scan.nextLine().toLowerCase(); - if(line.contains(searchStr)){ - return true; - } - } - - return false; + private static boolean foundMatchingDataOnFile(String fileContent, String searchStr) { + return fileContent.contains(searchStr); } - private static void fileSearchMatchingData(File file, List> itemsWithQuest) { try { - Scanner scanner = new Scanner(file); - + String fileContent = FileUtils.readFileToString(file, "UTF-8"); + List> copyItemsWithQuest = new ArrayList<>(itemsWithQuest); for(Pair iq : copyItemsWithQuest) { - scanner.reset(); - - if(foundMatchingDataOnFile(scanner, String.valueOf(iq.getLeft()))) { + if(foundMatchingDataOnFile(fileContent, String.valueOf(iq.getLeft()))) { itemsWithQuest.remove(iq); } } - } catch(FileNotFoundException e) {} + } catch(IOException ioe) { + System.out.println("Failed to read file: " + file.getAbsolutePath()); + ioe.printStackTrace(); + } } private static void printReportFileHeader() { printWriter.println(" # Report File autogenerated from the MapleQuestItemFetcher feature by Ronan Lana."); - printWriter.println(" # Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls."); + printWriter.println(" # Generated data takes into account several data info from the underlying DB, server source files and the server-side WZ.xmls."); printWriter.println(); } diff --git a/wz/Item.wz/Etc/0403.img.xml b/wz/Item.wz/Etc/0403.img.xml index c716d243fe..124dfea55a 100644 --- a/wz/Item.wz/Etc/0403.img.xml +++ b/wz/Item.wz/Etc/0403.img.xml @@ -9725,10 +9725,8 @@ - - - - + + @@ -9805,8 +9803,8 @@ - - + + diff --git a/wz/Map.wz/Map/Map1/101000301.img.xml b/wz/Map.wz/Map/Map1/101000301.img.xml index 84e959944c..0dedff9a41 100644 --- a/wz/Map.wz/Map/Map1/101000301.img.xml +++ b/wz/Map.wz/Map/Map1/101000301.img.xml @@ -10,7 +10,7 @@ - + diff --git a/wz/Quest.wz/Act.img.xml b/wz/Quest.wz/Act.img.xml index 1d797c4c02..fd93b2cc11 100644 --- a/wz/Quest.wz/Act.img.xml +++ b/wz/Quest.wz/Act.img.xml @@ -1,4 +1,4 @@ - + @@ -39231,7 +39231,7 @@ - + @@ -39258,130 +39258,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -39390,6 +39266,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Quest.wz/Check.img.xml b/wz/Quest.wz/Check.img.xml index a16066f0da..729f3aff70 100644 --- a/wz/Quest.wz/Check.img.xml +++ b/wz/Quest.wz/Check.img.xml @@ -62651,7 +62651,7 @@ - + @@ -62678,126 +62678,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -62806,6 +62686,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Quest.wz/QuestInfo.img.xml b/wz/Quest.wz/QuestInfo.img.xml index 6a50e7bcd1..a6c91d3fef 100644 --- a/wz/Quest.wz/QuestInfo.img.xml +++ b/wz/Quest.wz/QuestInfo.img.xml @@ -19525,7 +19525,7 @@ Able to proceed to 'Merry-go-round in Kampung' as next quest. - + diff --git a/wz/String.wz/Etc.img.xml b/wz/String.wz/Etc.img.xml index f28b14de75..14f985fcd8 100644 --- a/wz/String.wz/Etc.img.xml +++ b/wz/String.wz/Etc.img.xml @@ -973,8 +973,8 @@ - - + +