diff --git a/README.txt b/README.md similarity index 81% rename from README.txt rename to README.md index baf6fadbd1..6b3f49c015 100644 --- a/README.txt +++ b/README.md @@ -1,6 +1,7 @@ -MapleSolaxiaV2 +# MapleSolaxiaV2 +--- -Freelance developer: Ronan C. P. Lana +## Freelancer developer: Ronan C. P. Lana Credits are to be given too to Nexon(Duh!), the original MapleSolaxia staff and other colaborators, as just some changes/patches on the game were applied by myself, in which some of them diverged from the original v83 patch contents. @@ -12,25 +13,28 @@ In this project, many gameplay-wise issues generated from either the original WZ 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, to aim to get the best of the MapleStory of that era. ----- Download items ---- +--- +### Download items Server files: https://github.com/ronancpl/MapleSolaxiaV2 + Client files & general tools: https://drive.google.com/drive/folders/0BzDsHSr-0V4MYVJ0TWIxd05hYUk ----- Preparing the ambient ---- +--- +### Preparing the ambient The following link teaches on how to install a MapleStory v83 private server, however IT DIFFERS on what is used here: http://forum.ragezone.com/f428/maplestory-private-server-v83-741739/ Use that link ONLY AS AN ORIENTATION on where here things start to become ambiguous. Firstly, install all the general tools required to run the server: - - WampServer2.0i.exe -> recipient of the server. - - hamachi.msi -> used for establishing a tunnelling route for the server/client communication. - - mysql-workbench-gpl-5.2.39-win32 -> MySQL server component, will store the server's DB. +* WampServer2.0i.exe -> recipient of the server. +* hamachi.msi -> used for establishing a tunnelling route for the server/client communication. +* mysql-workbench-gpl-5.2.39-win32 -> MySQL server component, will store the server's DB. Now install the Java 7 Development Kit: - - jdk-7u79-windows-x64.exe - - netbeans-8.0.2-javase-windows.exe -> It's a NetBeans project, use other IDE at your own risk. +* jdk-7u79-windows-x64.exe +* netbeans-8.0.2-javase-windows.exe -> It's a NetBeans project, use other IDE at your own risk. Overwrite whenever prompted with the JAR files under "jce_policy-7/UnlimitedJCEPolicy" in these Java folders: C:\Program Files\Java\jre7\lib @@ -54,7 +58,8 @@ For Hamachi: Try opening it. It's that simple. Hamachi is optional, though. You don't have to install Hamachi if you want to make the server just for use on your own machine. However, if you want to let other players access your server, consider alternatively using port-forwarding methods. ----- Installing the SERVER ---- +--- +### Installing the SERVER Set the "MapleSolaxia" folder on a place of your preference. It is recommended for use "C:\Nexon\MapleSolaxia". @@ -62,11 +67,11 @@ Setting up the SQL: open MySQL Query Browser, and define these parameters at sta Server Host: localhost Port: 3306 Username: root Now it must be done CAREFULLY: - - File -> Open Script... -> Browse for "C:\MapleSolaxia\sql" -> db_database.sql, and execute it. - - File -> Open Script... -> Browse for "C:\MapleSolaxia\sql" -> db_drops.sql, and execute it. +1. File -> Open Script... -> Browse for "C:\MapleSolaxia\sql" -> db_database.sql, and execute it. +2. File -> Open Script... -> Browse for "C:\MapleSolaxia\sql" -> db_drops.sql, and execute it. Now it is OPTIONAL, you don't need to run it if you don't want, as it will simply change some NPC shops to set some new goods, not present in the original MapleStory, to sell: - - File -> Open Script... -> Browse for "C:\MapleSolaxia\sql" -> db_shopupdate.sql, and execute it. +3. File -> Open Script... -> Browse for "C:\MapleSolaxia\sql" -> db_shopupdate.sql, and execute it. At the end of the execution of these SQLs, you should have installed a database schema named "maplesolaxia". REGISTER YOUR FIRST ACCOUNT to be used in-game by creating manually a entry on the table "accounts" at that database with a login and a password. @@ -78,45 +83,47 @@ Inside the project, you may encounter some code errors. These happens because yo Finally, select "Clean and Build project" to build the JAR file for the MapleStory server. Once done, make sure both WampServer and Hamachi are on and functioning, and then execute "launch.bat" on the root of the project. If no errors were raised from this action, your MapleStory server is now online. ----- Installing the CLIENT ---- +--- +### Installing the CLIENT The client's set-up is quite straightforward: - - From "ManagerMsv83.exe", install MapleStory on your folder of preference (e.g. "C:\Nexon\MapleStory") and follow their instructions. - - Once done, erase these files: "HShield" (folder), "ASPLauncher.exe", "MapleStory.exe" and "patcher.exe". - - Extract into the client folder the "localhost.exe" from Localhostv83. - - Overwrite the original WZ files with the ones provided from "client_wz" folder on the Google Drive. +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". +3. Extract into the client folder the "localhost.exe" from Localhostv83. +4. Overwrite the original WZ files with the ones provided from "client_wz" folder on the Google Drive. If you are not using "localhost" as the target IP on the server's config file, you will need to HEX-EDIT "localhost.exe" to fetch your IP. Track down all IP locations by searching for "Text String" "127.0.0.1", and applying the changes wherever it fits. To hex-edit, install the Neo Hex Editor from "free-hex-editor-neo.exe" and follow their instructions. Once done, open "localhost.exe" for editing and overwrite the IP values under the 3 addresses. Save the changes and exit the editor. Open the "localhost.exe" client. If by any means the program did not open, and checking que server log your ping has been listened and you are using Windows 8 or 10, it probably might be some compatibility issue. Extract "lolwut.exe" from "lolwut-v0.01.rar" and place it on the MapleStory client folder ("C:\Nexon\MapleStory"). Your "localhost.exe" property settings must follow these: - - Run in compatibility mode: Windows 7; - - Unchecked reduced color mode; - - 640 x 480 resolution; - - Unchecked disable display on high DPI settings; - - Run as an administrator; - - Opening "lolwut.exe", use Fraysa's method. +* Run in compatibility mode: Windows 7; +* Unchecked reduced color mode; +* 640 x 480 resolution; +* Unchecked disable display on high DPI settings; +* Run as an administrator; +* Opening "lolwut.exe", use Fraysa's method. Important: should the client being refused to connect to the game server, it may be because firewall issues. Head to the end of this file to proceed to enabling this connection with the computer's firewall. Alternatively, one can deactivate the firewall and try opening the client again. ----- Important note about CLIENT EDITING ---- +--- +### Important note about CLIENT EDITING DO NOT USE the server's XMLs for reimporting into the client's WZ, it WILL generate some kind of bugs afterwards. - - Use instead the HaRepacker 4.2.4, encryption "GMS (old)". - - Open the desired WZ for editing and, USING THE UI, make the desired changes. - - Save the changed WZ, overwriting the original content at the client folder. - - Finally, RE-EXPORT ("Private Server..." exporting option) the changed XMLs into the server's WZ.XML files, overwriting the old contents. +* Use instead the HaRepacker 4.2.4, encryption "GMS (old)". +* Open the desired WZ for editing and, USING THE UI, make the desired changes. +* Save the changed WZ, overwriting the original content at the client folder. +* Finally, RE-EXPORT ("Private Server..." exporting option) the changed XMLs into the server's WZ.XML files, overwriting the old contents. These steps are IMPORTANT to maintain synchronization between the server and client modules. As an example of client WZ editing, consider the MobBookUpdate project I developed, for updating all reported drop data of the mobs in the game based on the current drop data on the database: - - Open the MobBookUpdate project on NetBeans, located at "C:\Nexon\MapleSolaxia\MobBookUpdate", and build it. - - At the subfolder "lib", copy the file "MonsterBook.img.xml". This is from the original WZ v83. - - Paste it on the "dist" subfolder. - - Inside "dist", open the command prompt by alt+right clicking there. - - Execute "java -jar MobBookUpdate.jar". It will generate a "MonsterBook_updated.img.xml" file. - - At last, overwrite the "MonsterBook.img.xml" on "C:\Nexon\MapleSolaxia\wz\String.wz" with this file, renaming it back to "MonsterBook.img.xml". +* Open the MobBookUpdate project on NetBeans, located at "C:\Nexon\MapleSolaxia\MobBookUpdate", and build it. +* At the subfolder "lib", copy the file "MonsterBook.img.xml". This is from the original WZ v83. +* Paste it on the "dist" subfolder. +* Inside "dist", open the command prompt by alt+right clicking there. +* Execute "java -jar MobBookUpdate.jar". It will generate a "MonsterBook_updated.img.xml" file. +* At last, overwrite the "MonsterBook.img.xml" on "C:\Nexon\MapleSolaxia\wz\String.wz" with this file, renaming it back to "MonsterBook.img.xml". At this point, the server-side Monster Book has been updated with the current state of the database's drop data. Then, open HaRepacker 4.2.2 and load "String.wz" from "C:\Nexon\MapleStory". Drop the "MonsterBook.img" node by removing it from the hierarchy tree, then (CONTRARY TO WHAT SHOULD BE DONE NORMALLY!) import the server's "MonsterBook.img.xml". @@ -124,7 +131,8 @@ Take note that this is absolutely dangerous if done unwary. Once the MonsterBook Save the changes and overwrite the older WZ on the MapleStory client folder. ----- Portforwarding the SERVER ---- +--- +### Portforwarding the SERVER To use portforward, you will need to have permission to change things on the LAN router. Access yor router using the Internet browser. URLs vary accordingly with the manufacturer. To discover it, open the command prompt and type "ipconfig" and search for the "default gateway" field. The IP shown there is the URL needed to access the router. Also, look for the IP given to your machine (aka "IPv4 address" field), which will be the server one. diff --git a/build/built-jar.properties b/build/built-jar.properties index 7aac02946f..77d5c4fb19 100644 --- a/build/built-jar.properties +++ b/build/built-jar.properties @@ -1,4 +1,4 @@ -#Fri, 15 Sep 2017 16:29:27 -0300 +#Fri, 22 Sep 2017 18:20:17 -0300 C\:\\Nexon\\MapleSolaxia\\MapleSolaxiaV2= diff --git a/build/classes/client/MapleCharacter$1.class b/build/classes/client/MapleCharacter$1.class index caf87a4cd0..a361b3bdf9 100644 Binary files a/build/classes/client/MapleCharacter$1.class and b/build/classes/client/MapleCharacter$1.class differ diff --git a/build/classes/client/MapleCharacter$10.class b/build/classes/client/MapleCharacter$10.class index 8c32c62936..ee6100e0ef 100644 Binary files a/build/classes/client/MapleCharacter$10.class and b/build/classes/client/MapleCharacter$10.class differ diff --git a/build/classes/client/MapleCharacter$11.class b/build/classes/client/MapleCharacter$11.class index 7266dde9ae..d99275b03c 100644 Binary files a/build/classes/client/MapleCharacter$11.class and b/build/classes/client/MapleCharacter$11.class differ diff --git a/build/classes/client/MapleCharacter$12.class b/build/classes/client/MapleCharacter$12.class index a46244c6f8..7a727bbf6e 100644 Binary files a/build/classes/client/MapleCharacter$12.class and b/build/classes/client/MapleCharacter$12.class differ diff --git a/build/classes/client/MapleCharacter$13.class b/build/classes/client/MapleCharacter$13.class index d1adf7cdfe..f4c48c73ad 100644 Binary files a/build/classes/client/MapleCharacter$13.class and b/build/classes/client/MapleCharacter$13.class differ diff --git a/build/classes/client/MapleCharacter$14.class b/build/classes/client/MapleCharacter$14.class index 874a61d710..016ea1c511 100644 Binary files a/build/classes/client/MapleCharacter$14.class and b/build/classes/client/MapleCharacter$14.class differ diff --git a/build/classes/client/MapleCharacter$15.class b/build/classes/client/MapleCharacter$15.class index 96b29cacec..03162d5e85 100644 Binary files a/build/classes/client/MapleCharacter$15.class and b/build/classes/client/MapleCharacter$15.class differ diff --git a/build/classes/client/MapleCharacter$16.class b/build/classes/client/MapleCharacter$16.class index 170c84834c..b2d8b49392 100644 Binary files a/build/classes/client/MapleCharacter$16.class and b/build/classes/client/MapleCharacter$16.class differ diff --git a/build/classes/client/MapleCharacter$17.class b/build/classes/client/MapleCharacter$17.class index 7d4ae14502..7c0a5e73ab 100644 Binary files a/build/classes/client/MapleCharacter$17.class and b/build/classes/client/MapleCharacter$17.class differ diff --git a/build/classes/client/MapleCharacter$18.class b/build/classes/client/MapleCharacter$18.class index 42db08cbac..954814eb6e 100644 Binary files a/build/classes/client/MapleCharacter$18.class and b/build/classes/client/MapleCharacter$18.class differ diff --git a/build/classes/client/MapleCharacter$19.class b/build/classes/client/MapleCharacter$19.class index bd776086cc..0003e3b34a 100644 Binary files a/build/classes/client/MapleCharacter$19.class and b/build/classes/client/MapleCharacter$19.class differ diff --git a/build/classes/client/MapleCharacter$2.class b/build/classes/client/MapleCharacter$2.class index d5dfbb8d00..576c9b7de2 100644 Binary files a/build/classes/client/MapleCharacter$2.class and b/build/classes/client/MapleCharacter$2.class differ diff --git a/build/classes/client/MapleCharacter$3.class b/build/classes/client/MapleCharacter$3.class index ff78325b79..110946bc58 100644 Binary files a/build/classes/client/MapleCharacter$3.class and b/build/classes/client/MapleCharacter$3.class differ diff --git a/build/classes/client/MapleCharacter$4.class b/build/classes/client/MapleCharacter$4.class index cf9617d27b..2552774c5b 100644 Binary files a/build/classes/client/MapleCharacter$4.class and b/build/classes/client/MapleCharacter$4.class differ diff --git a/build/classes/client/MapleCharacter$5.class b/build/classes/client/MapleCharacter$5.class index 0b4717dcd1..54a45b8fcf 100644 Binary files a/build/classes/client/MapleCharacter$5.class and b/build/classes/client/MapleCharacter$5.class differ diff --git a/build/classes/client/MapleCharacter$6.class b/build/classes/client/MapleCharacter$6.class index 8a47bae146..492b11ddb5 100644 Binary files a/build/classes/client/MapleCharacter$6.class and b/build/classes/client/MapleCharacter$6.class differ diff --git a/build/classes/client/MapleCharacter$7.class b/build/classes/client/MapleCharacter$7.class index 73c3c5bc00..22443c05cf 100644 Binary files a/build/classes/client/MapleCharacter$7.class and b/build/classes/client/MapleCharacter$7.class differ diff --git a/build/classes/client/MapleCharacter$8.class b/build/classes/client/MapleCharacter$8.class index 0ef2e678ff..26802da369 100644 Binary files a/build/classes/client/MapleCharacter$8.class and b/build/classes/client/MapleCharacter$8.class differ diff --git a/build/classes/client/MapleCharacter$9.class b/build/classes/client/MapleCharacter$9.class index 91c2d5d19b..6696ed12c1 100644 Binary files a/build/classes/client/MapleCharacter$9.class and b/build/classes/client/MapleCharacter$9.class differ diff --git a/build/classes/client/MapleCharacter$CancelCooldownAction.class b/build/classes/client/MapleCharacter$CancelCooldownAction.class deleted file mode 100644 index 4c50e4828d..0000000000 Binary files a/build/classes/client/MapleCharacter$CancelCooldownAction.class and /dev/null differ diff --git a/build/classes/client/MapleCharacter$FameStatus.class b/build/classes/client/MapleCharacter$FameStatus.class index 1497a01a75..a01230969c 100644 Binary files a/build/classes/client/MapleCharacter$FameStatus.class and b/build/classes/client/MapleCharacter$FameStatus.class differ diff --git a/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class b/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class index 0d38713385..90cafdd621 100644 Binary files a/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class and b/build/classes/client/MapleCharacter$MapleBuffStatValueHolder.class differ diff --git a/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class b/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class index b046e52048..43d7af5ada 100644 Binary files a/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class and b/build/classes/client/MapleCharacter$MapleCoolDownValueHolder.class differ diff --git a/build/classes/client/MapleCharacter$SkillEntry.class b/build/classes/client/MapleCharacter$SkillEntry.class index 68cd2a2e25..df0d9045a6 100644 Binary files a/build/classes/client/MapleCharacter$SkillEntry.class and b/build/classes/client/MapleCharacter$SkillEntry.class differ diff --git a/build/classes/client/MapleCharacter.class b/build/classes/client/MapleCharacter.class index 30fd6aeac2..95c5514978 100644 Binary files a/build/classes/client/MapleCharacter.class and b/build/classes/client/MapleCharacter.class differ diff --git a/build/classes/client/MapleClient.class b/build/classes/client/MapleClient.class index 393e6c9339..02b85613d6 100644 Binary files a/build/classes/client/MapleClient.class and b/build/classes/client/MapleClient.class differ diff --git a/build/classes/client/command/Commands$1.class b/build/classes/client/command/Commands$1.class index 5edc72f238..351ebdf676 100644 Binary files a/build/classes/client/command/Commands$1.class and b/build/classes/client/command/Commands$1.class differ diff --git a/build/classes/client/command/Commands.class b/build/classes/client/command/Commands.class index 06c37163dd..f30be0f8bd 100644 Binary files a/build/classes/client/command/Commands.class and b/build/classes/client/command/Commands.class differ diff --git a/build/classes/constants/ItemConstants.class b/build/classes/constants/ItemConstants.class index 0bd1db2154..53fb5d7866 100644 Binary files a/build/classes/constants/ItemConstants.class and b/build/classes/constants/ItemConstants.class differ diff --git a/build/classes/constants/ServerConstants.class b/build/classes/constants/ServerConstants.class index a5be0a3d27..b695a8ccb4 100644 Binary files a/build/classes/constants/ServerConstants.class and b/build/classes/constants/ServerConstants.class differ diff --git a/build/classes/net/server/CouponWorker.class b/build/classes/net/server/CouponWorker.class deleted file mode 100644 index 79498e6aa7..0000000000 Binary files a/build/classes/net/server/CouponWorker.class and /dev/null differ diff --git a/build/classes/net/server/RankingWorker.class b/build/classes/net/server/RankingWorker.class deleted file mode 100644 index e0745771b8..0000000000 Binary files a/build/classes/net/server/RankingWorker.class and /dev/null differ diff --git a/build/classes/net/server/Server$1.class b/build/classes/net/server/Server$1.class index 1e0c156886..06eb077f6a 100644 Binary files a/build/classes/net/server/Server$1.class and b/build/classes/net/server/Server$1.class differ diff --git a/build/classes/net/server/Server.class b/build/classes/net/server/Server.class index 0491b05c71..4bef61ba52 100644 Binary files a/build/classes/net/server/Server.class and b/build/classes/net/server/Server.class differ diff --git a/build/classes/net/server/channel/handlers/CancelBuffHandler.class b/build/classes/net/server/channel/handlers/CancelBuffHandler.class index f44249945f..2fe33a80ef 100644 Binary files a/build/classes/net/server/channel/handlers/CancelBuffHandler.class and b/build/classes/net/server/channel/handlers/CancelBuffHandler.class differ diff --git a/build/classes/net/server/channel/handlers/CloseRangeDamageHandler.class b/build/classes/net/server/channel/handlers/CloseRangeDamageHandler.class index 194eb2c5a6..55ecccc809 100644 Binary files a/build/classes/net/server/channel/handlers/CloseRangeDamageHandler.class and b/build/classes/net/server/channel/handlers/CloseRangeDamageHandler.class differ diff --git a/build/classes/net/server/channel/handlers/EnterCashShopHandler.class b/build/classes/net/server/channel/handlers/EnterCashShopHandler.class index 068b348631..4596d166cc 100644 Binary files a/build/classes/net/server/channel/handlers/EnterCashShopHandler.class and b/build/classes/net/server/channel/handlers/EnterCashShopHandler.class differ diff --git a/build/classes/net/server/channel/handlers/EnterMTSHandler.class b/build/classes/net/server/channel/handlers/EnterMTSHandler.class index 32b70e1dbe..a36be5dc2c 100644 Binary files a/build/classes/net/server/channel/handlers/EnterMTSHandler.class and b/build/classes/net/server/channel/handlers/EnterMTSHandler.class differ diff --git a/build/classes/net/server/channel/handlers/MagicDamageHandler.class b/build/classes/net/server/channel/handlers/MagicDamageHandler.class index d0e9eff264..024a19bc55 100644 Binary files a/build/classes/net/server/channel/handlers/MagicDamageHandler.class and b/build/classes/net/server/channel/handlers/MagicDamageHandler.class differ diff --git a/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class b/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class index d2a21fd807..651f198286 100644 Binary files a/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class and b/build/classes/net/server/channel/handlers/PlayerInteractionHandler$Action.class differ diff --git a/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class b/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class index e94e3ffd41..a043aa9590 100644 Binary files a/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class and b/build/classes/net/server/channel/handlers/PlayerInteractionHandler.class differ diff --git a/build/classes/net/server/channel/handlers/PlayerLoggedinHandler.class b/build/classes/net/server/channel/handlers/PlayerLoggedinHandler.class index dcf31ec9db..98843c7969 100644 Binary files a/build/classes/net/server/channel/handlers/PlayerLoggedinHandler.class and b/build/classes/net/server/channel/handlers/PlayerLoggedinHandler.class differ diff --git a/build/classes/net/server/channel/handlers/RangedAttackHandler.class b/build/classes/net/server/channel/handlers/RangedAttackHandler.class index cfbf816f4f..2a33769d85 100644 Binary files a/build/classes/net/server/channel/handlers/RangedAttackHandler.class and b/build/classes/net/server/channel/handlers/RangedAttackHandler.class differ diff --git a/build/classes/net/server/channel/handlers/SpecialMoveHandler.class b/build/classes/net/server/channel/handlers/SpecialMoveHandler.class index 07cb375fe2..bd888d1b04 100644 Binary files a/build/classes/net/server/channel/handlers/SpecialMoveHandler.class and b/build/classes/net/server/channel/handlers/SpecialMoveHandler.class differ diff --git a/build/classes/net/server/world/World$1.class b/build/classes/net/server/world/World$1.class index 178a4b554d..af99445edf 100644 Binary files a/build/classes/net/server/world/World$1.class and b/build/classes/net/server/world/World$1.class differ diff --git a/build/classes/net/server/world/World.class b/build/classes/net/server/world/World.class index 3909cebb55..e2e7aa6861 100644 Binary files a/build/classes/net/server/world/World.class and b/build/classes/net/server/world/World.class differ diff --git a/build/classes/server/MapleInventoryManipulator.class b/build/classes/server/MapleInventoryManipulator.class index 6fb50376e4..53c6d95c1f 100644 Binary files a/build/classes/server/MapleInventoryManipulator.class and b/build/classes/server/MapleInventoryManipulator.class differ diff --git a/build/classes/server/MapleItemInformationProvider$1.class b/build/classes/server/MapleItemInformationProvider$1.class index f3a4d99ac8..d0fbe4a32f 100644 Binary files a/build/classes/server/MapleItemInformationProvider$1.class and b/build/classes/server/MapleItemInformationProvider$1.class differ diff --git a/build/classes/server/MapleItemInformationProvider$RewardItem.class b/build/classes/server/MapleItemInformationProvider$RewardItem.class index dfa17dd5b4..b9223961de 100644 Binary files a/build/classes/server/MapleItemInformationProvider$RewardItem.class and b/build/classes/server/MapleItemInformationProvider$RewardItem.class differ diff --git a/build/classes/server/MapleItemInformationProvider$scriptedItem.class b/build/classes/server/MapleItemInformationProvider$scriptedItem.class index 278b10a825..d5ca87bc14 100644 Binary files a/build/classes/server/MapleItemInformationProvider$scriptedItem.class and b/build/classes/server/MapleItemInformationProvider$scriptedItem.class differ diff --git a/build/classes/server/MapleItemInformationProvider.class b/build/classes/server/MapleItemInformationProvider.class index 6f8ffb3411..cff13cffe9 100644 Binary files a/build/classes/server/MapleItemInformationProvider.class and b/build/classes/server/MapleItemInformationProvider.class differ diff --git a/build/classes/server/MapleShop.class b/build/classes/server/MapleShop.class index 2317422a10..07c1f37285 100644 Binary files a/build/classes/server/MapleShop.class and b/build/classes/server/MapleShop.class differ diff --git a/build/classes/server/MapleStatEffect$CancelEffectAction.class b/build/classes/server/MapleStatEffect$CancelEffectAction.class deleted file mode 100644 index c2ddbee840..0000000000 Binary files a/build/classes/server/MapleStatEffect$CancelEffectAction.class and /dev/null differ diff --git a/build/classes/server/MapleStatEffect.class b/build/classes/server/MapleStatEffect.class index 508578713e..7ddf2b4401 100644 Binary files a/build/classes/server/MapleStatEffect.class and b/build/classes/server/MapleStatEffect.class differ diff --git a/build/classes/server/maps/HiredMerchant$1.class b/build/classes/server/maps/HiredMerchant$1.class deleted file mode 100644 index 7674d57d27..0000000000 Binary files a/build/classes/server/maps/HiredMerchant$1.class and /dev/null differ diff --git a/build/classes/server/maps/HiredMerchant$SoldItem.class b/build/classes/server/maps/HiredMerchant$SoldItem.class index b0763fb4cd..ec0540adc6 100644 Binary files a/build/classes/server/maps/HiredMerchant$SoldItem.class and b/build/classes/server/maps/HiredMerchant$SoldItem.class differ diff --git a/build/classes/server/maps/HiredMerchant.class b/build/classes/server/maps/HiredMerchant.class index e8e60ba291..d515db0951 100644 Binary files a/build/classes/server/maps/HiredMerchant.class and b/build/classes/server/maps/HiredMerchant.class differ diff --git a/build/classes/tools/MaplePacketCreator$1.class b/build/classes/tools/MaplePacketCreator$1.class index a421a4f622..bf10881eaf 100644 Binary files a/build/classes/tools/MaplePacketCreator$1.class and b/build/classes/tools/MaplePacketCreator$1.class differ diff --git a/build/classes/tools/MaplePacketCreator$2.class b/build/classes/tools/MaplePacketCreator$2.class index aae1b45fd4..92a5a20459 100644 Binary files a/build/classes/tools/MaplePacketCreator$2.class and b/build/classes/tools/MaplePacketCreator$2.class differ diff --git a/build/classes/tools/MaplePacketCreator.class b/build/classes/tools/MaplePacketCreator.class index 6cb01864a4..793fdd2e25 100644 Binary files a/build/classes/tools/MaplePacketCreator.class and b/build/classes/tools/MaplePacketCreator.class differ diff --git a/dist/MapleSolaxia.jar b/dist/MapleSolaxia.jar index 0a64f0f73a..9a07659f5a 100644 Binary files a/dist/MapleSolaxia.jar and b/dist/MapleSolaxia.jar differ diff --git a/docs/feature_list.txt b/docs/feature_list.txt index 556078e9df..44e270d5a3 100644 --- a/docs/feature_list.txt +++ b/docs/feature_list.txt @@ -22,7 +22,7 @@ PQs: * CWKPQ as Expedition-based event 100%. * Expeditions: Scarga/Horntail/Showa/Zakum/Pinkbean 100%. * GuildPQ 100% + Guild queue with multi-lobby systems available. -* Brand-new PQ: Boss Rush PQ 100%. +* Brand-new PQs: BossRushPQ, CafePQ 100%. * Mu Lung Dojo 100%. * BalrogPQ semi-functional. * Capt. Latanica remade as an event (parties can now fight the boss). @@ -41,7 +41,7 @@ Cash & Items: * EXP/DROP/Cosmetic Coupons 100%. * EXP/DROP coupons now appears as a buff effect when on active time. * Great deal of cash items functional. -* New scroll: antibanish. Used only in cases where bosses send a player back to town. +* New scroll: antibanish. For use only in cases where bosses send a player back to town. PQ potentials: * Lobby system - Multiple PQ instances on same channel. diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index bec6bce3bf..fe419a26d9 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -534,4 +534,14 @@ Explorer mount quest como evento, com timeout e expulsa o player se o hog morre. 14 - 15 Setembro 2017, Adicionado AmoriaPQ. -Consertado um problema em Inventory Sort que lançava exceção caso tivesse que ordenar vetores vazios. \ No newline at end of file +Consertado um problema em Inventory Sort que lançava exceção caso tivesse que ordenar vetores vazios. + +16 Setembro 2017, +Refatorado sistema de schedules para hired merchants. Agora o server passa a instanciar as chamadas às funções que fecham as hired merchants, ao invés de instanciar uma task para cada. + +18 - 21 Setembro 2017, +Refatorado sistema de schedules para skill cooldowns e buff expiretimes. +Completamente reestruturado sistema de buffs. Nova flag permite permanência de buffs mais fortes mesmo que novos buffs surjam. + +22 Setembro 2017, +Adicionado buffs para GPQ. \ No newline at end of file diff --git a/handbook/Use.txt b/handbook/Use.txt index 8bc6fd8a0e..da05b2f5ec 100644 --- a/handbook/Use.txt +++ b/handbook/Use.txt @@ -365,6 +365,10 @@ 2022453 - Fireworks - The fireworks for celebrating MV's defeat. Speed +5, Physical & Magic Attack +5 for 20 min. 2022454 - Cygnus's Blessing - Once I completed Cygnus' Book, the spirit's power covered me and blessed me. It increased my Attack Rate by 10, Physical Defense Rate by 80 and Speed by 5 for 10 minutes. 2022538 - Red Easter Egg - A freshly boiled egg colored in red. Recovers 400 HP and MP. +2023000 - Sharenian Grounds : Fortitude - From the ancient remains, an unknown blessing grants you fortitude. Increases Weapon Attack & Weapon Defense. +2023001 - Sharenian Grounds : Intellect - From the ancient remains, an unknown blessing grants you intellect. Increases Magic Attack & Magic Defense. +2023002 - Sharenian Grounds : Endurance - From the ancient remains, an unknown blessing grants you endurance. Increases Weapon Defense & Magic Defense. +2023003 - Sharenian Grounds : Swiftness - From the ancient remains, an unknown blessing grants you swiftness. Increases Accuracy & Avoidability. 2030000 - Return Scroll - Nearest Town - Returns you to the nearest town. 2030001 - Return Scroll to Lith Harbor - Returns you to Lith Harbor. 2030002 - Return Scroll to Ellinia - Returns you to Ellinia. diff --git a/scripts/event/GuildQuest.js b/scripts/event/GuildQuest.js index 70e0360191..157fc3986f 100644 --- a/scripts/event/GuildQuest.js +++ b/scripts/event/GuildQuest.js @@ -166,19 +166,23 @@ function setup(level, lobbyid) { return eim; } -/* function isTeamAllJobs(eim) { var eventJobs = eim.getEventPlayersJobs(); var rangeJobs = parseInt('111110', 2); return ((eventJobs & rangeJobs) == rangeJobs); } -*/ function afterSetup(eim) { var leader = em.getChannelServer().getPlayerStorage().getCharacterById(eim.getLeaderId()); - if(leader != null) + if(leader != null) { eim.setProperty("guild", "" + leader.getGuildId()); + } + + if(isTeamAllJobs(eim)) { + var rnd = Math.floor(Math.random() * 4); + eim.applyEventPlayersItemBuff(2023000 + rnd); + } } function respawnStages(eim) {} @@ -209,7 +213,12 @@ function scheduledTimeout(eim) { } } -function playerUnregistered(eim, player) {} +function playerUnregistered(eim, player) { + player.cancelEffect(2023000); + player.cancelEffect(2023001); + player.cancelEffect(2023002); + player.cancelEffect(2023003); +} function playerExit(eim, player) { eim.unregisterPlayer(player); diff --git a/scripts/npc/1012117.js b/scripts/npc/1012117.js index 0112b3eb55..a2d88c8a31 100644 --- a/scripts/npc/1012117.js +++ b/scripts/npc/1012117.js @@ -26,8 +26,8 @@ */ var status = 0; -var mhair = Array(30040, 30050, 30100, 30130, 30180, 30220, 30260, 30330, 30350, 30580); -var fhair = Array(31020, 31160, 31180, 31220, 31290, 31330, 31420, 31440, 31480, 31590); +var mhair = Array(30100, 30850, 30890); +var fhair = Array(31180, 31420, 31870, 31400, 31880, 31820, 31860, 31420, 31940, 34000, 31890); var hairnew = Array(); function start() { diff --git a/scripts/npc/commands.js b/scripts/npc/commands.js index 2af2be1c5b..7b5376f36d 100644 --- a/scripts/npc/commands.js +++ b/scripts/npc/commands.js @@ -92,7 +92,7 @@ function writeSolaxiaCommandsLv4() { //SuperGM addCommand("pap", ""); addCommand("pianus", ""); addCommand("cake", ""); - addCommand("playernpc", ""); + //addCommand("playernpc", ""); } function writeSolaxiaCommandsLv3() { //GM diff --git a/sql/db_drops.sql b/sql/db_drops.sql index 0dc030637c..90a1157b74 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -17893,10 +17893,14 @@ USE `maplesolaxia`; (9400121, 1332022, 1, 1, 0, 40000), (9400121, 1332027, 1, 1, 0, 40000), (9400121, 1472033, 1, 1, 0, 40000), -(9400590, 1122059, 1, 1, 0, 40000), -(9400591, 1122059, 1, 1, 0, 40000), -(9400592, 1122059, 1, 1, 0, 40000), -(9400593, 1122059, 1, 1, 0, 40000), +(9400590, 1122059, 1, 1, 0, 8000), +(9400590, 1012076, 1, 1, 0, 20000), +(9400591, 1122059, 1, 1, 0, 8000), +(9400591, 1412040, 1, 1, 0, 20000), +(9400592, 1122059, 1, 1, 0, 8000), +(9400592, 1022082, 1, 1, 0, 20000), +(9400593, 1122059, 1, 1, 0, 8000), +(9400593, 1122015, 1, 1, 0, 20000), (8200012, 4000458, 1, 1, 0, 200000), (8200012, 4000459, 1, 1, 0, 200000), (8200012, 4130012, 1, 1, 0, 3000), @@ -21121,6 +21125,7 @@ USE `maplesolaxia`; DELETE FROM `drop_data` WHERE itemid=4000435; DELETE FROM `drop_data` WHERE itemid=4032192; DELETE FROM `drop_data` WHERE itemid=8143000; + DELETE FROM `drop_data` WHERE itemid=2094101; # delete all unused content on reactor drop data DELETE FROM `reactordrops` WHERE itemid=1102260; diff --git a/sql/db_shopupdate.sql b/sql/db_shopupdate.sql index 31b9ea153d..a616b7b8f0 100644 --- a/sql/db_shopupdate.sql +++ b/sql/db_shopupdate.sql @@ -221,4 +221,5 @@ INSERT INTO `shopitems` ( `shopid`, `itemid`, `price`, `position`) VALUES (1337, 2044025, 1, 80), (1337, 2043712, 1, 81), (1337, 2340000, 1, 82), -(1337, 2040807, 1, 83); \ No newline at end of file +(1337, 2040807, 1, 83), +(1337, 2210032, 1, 84); \ No newline at end of file diff --git a/src/client/MapleBuffStat.java b/src/client/MapleBuffStat.java index a6c930aeda..fefdb8f39d 100644 --- a/src/client/MapleBuffStat.java +++ b/src/client/MapleBuffStat.java @@ -48,7 +48,7 @@ public enum MapleBuffStat { AURA(0x40000L), CONFUSE(0x80000L), - // ---- COUPON feature (was unused anyway) ---- + // ------ COUPON feature ------ COUPON_EXP1(0x100000L), COUPON_EXP2(0x200000L), diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index 4487caf21e..af63e2890e 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -22,7 +22,6 @@ package client; import java.awt.Point; -import java.lang.ref.WeakReference; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -45,7 +44,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -//import java.util.TimeZone; +import java.util.Comparator; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Lock; import java.util.concurrent.ScheduledFuture; @@ -239,21 +238,27 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private Map activeCoupons = new LinkedHashMap<>(); private Map activeCouponRates = new LinkedHashMap<>(); private EnumMap effects = new EnumMap<>(MapleBuffStat.class); + private Map buffEffectsCount = new LinkedHashMap<>(); + private Map> buffEffects = new LinkedHashMap<>(); + private Map buffExpires = new LinkedHashMap<>(); private Map keymap = new LinkedHashMap<>(); private Map summons = new LinkedHashMap<>(); - private Map coolDowns = new LinkedHashMap<>(50); + private Map coolDowns = new LinkedHashMap<>(); private EnumMap diseases = new EnumMap<>(MapleDisease.class); private Map doors = new LinkedHashMap<>(); private ScheduledFuture dragonBloodSchedule; private ScheduledFuture hpDecreaseTask; private ScheduledFuture beholderHealingSchedule, beholderBuffSchedule, BerserkSchedule; - private ScheduledFuture expiretask; + private ScheduledFuture skillCooldownTask = null; + private ScheduledFuture buffExpireTask = null; + private ScheduledFuture itemExpireTask = null; private ScheduledFuture recoveryTask = null; private ScheduledFuture extraRecoveryTask = null; private ScheduledFuture chairRecoveryTask = null; private ScheduledFuture pendantOfSpirit = null; //1122017 private List> timers = new ArrayList<>(); private Lock chrLock = new ReentrantLock(); + private Lock effLock = new ReentrantLock(); private Lock petLock = new ReentrantLock(); private NumberFormat nf = new DecimalFormat("#,###,###,###"); private Map> excluded = new LinkedHashMap<>(); @@ -423,11 +428,18 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { npcCd = d; } - public void addCooldown(int skillId, long startTime, long length, ScheduledFuture timer) { - if (this.coolDowns.containsKey(Integer.valueOf(skillId))) { - this.coolDowns.remove(skillId); + public void addCooldown(int skillId, long startTime, long length) { + effLock.lock(); + chrLock.lock(); + try { + if (this.coolDowns.containsKey(Integer.valueOf(skillId))) { + this.coolDowns.remove(Integer.valueOf(skillId)); + } + this.coolDowns.put(Integer.valueOf(skillId), new MapleCoolDownValueHolder(skillId, startTime, length)); + } finally { + chrLock.unlock(); + effLock.unlock(); } - this.coolDowns.put(Integer.valueOf(skillId), new MapleCoolDownValueHolder(skillId, startTime, length, timer)); } public void addCrushRing(MapleRing r) { @@ -720,36 +732,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return maxbasedamage; } - public void cancelAllBuffs(boolean disconnect) { - if (disconnect) { - chrLock.lock(); - try { - effects.clear(); - } finally { - chrLock.unlock(); - } - } else { - List mbsvhList; - - chrLock.lock(); - try { - mbsvhList = new ArrayList<>(effects.values()); - } finally { - chrLock.unlock(); - } - - for (MapleBuffStatValueHolder mbsvh : mbsvhList) { - cancelEffect(mbsvh.effect, false, mbsvh.startTime); - } - } - } - - public synchronized void cancelBuffStats(MapleBuffStat stat) { - List buffStatList = Arrays.asList(stat); - dropBuffStats(deregisterBuffStats(buffStatList)); - cancelPlayerBuffs(buffStatList); - } - public void setCombo(short count) { if (count < combocounter) { cancelEffectFromBuffStat(MapleBuffStat.ARAN_COMBO); @@ -801,18 +783,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { this.map = c.getChannelServer().getMapFactory().getMap(getMapId()); } - public void cancelBuffEffects() { - chrLock.lock(); - try { - for (MapleBuffStatValueHolder mbsvh : effects.values()) { - mbsvh.schedule.cancel(false); - } - this.effects.clear(); - } finally { - chrLock.unlock(); - } - } - public String getMedalText() { String medal = ""; final Item medalItem = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -49); @@ -822,116 +792,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return medal; } - public static class CancelCooldownAction implements Runnable { - - private int skillId; - private WeakReference target; - - public CancelCooldownAction(MapleCharacter target, int skillId) { - this.target = new WeakReference<>(target); - this.skillId = skillId; - } - - @Override - public void run() { - MapleCharacter realTarget = target.get(); - if (realTarget != null) { - realTarget.removeCooldown(skillId); - realTarget.client.announce(MaplePacketCreator.skillCooldown(skillId, 0)); - } - } - } - - public void cancelEffect(int itemId) { - cancelEffect(MapleItemInformationProvider.getInstance().getItemEffect(itemId), false, -1); - } - - public synchronized void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) { - cancelEffect(effect, overwrite, startTime, true); - } - - private void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime, boolean firstCancel) { - dropBuffStats(cancelEffectInternal(effect, overwrite, startTime)); - } - - private List cancelEffectInternal(MapleStatEffect effect, boolean overwrite, long startTime) { - List buffstats; - if (!overwrite) { - buffstats = getBuffStats(effect, startTime); - } else { - List> statups = effect.getStatups(); - buffstats = new ArrayList<>(statups.size()); - for (Pair statup : statups) { - buffstats.add(statup.getLeft()); - } - } - if (effect.isMagicDoor()) { - MapleDoor destroyDoor; - - chrLock.lock(); - try { - destroyDoor = doors.remove(this.getId()); - } finally { - chrLock.unlock(); - } - - if (destroyDoor != null) { - destroyDoor.getTarget().removeMapObject(destroyDoor.getAreaDoor()); - destroyDoor.getTown().removeMapObject(destroyDoor.getTownDoor()); - - for (MapleCharacter chr : destroyDoor.getTarget().getCharacters()) { - destroyDoor.getAreaDoor().sendDestroyData(chr.getClient()); - } - for (MapleCharacter chr : destroyDoor.getTown().getCharacters()) { - destroyDoor.getTownDoor().sendDestroyData(chr.getClient()); - } - - if (party != null) { - for (MaplePartyCharacter partyMembers : getParty().getMembers()) { - partyMembers.getPlayer().removeDoor(this.getId()); - partyMembers.removeDoor(this.getId()); - } - silentPartyUpdate(); - } - } - } - - List toCancel = deregisterBuffStats(buffstats); - if (effect.getSourceId() == Spearman.HYPER_BODY || effect.getSourceId() == GM.HYPER_BODY || effect.getSourceId() == SuperGM.HYPER_BODY) { - List> statup = new ArrayList<>(4); - statup.add(new Pair<>(MapleStat.HP, Math.min(hp, maxhp))); - statup.add(new Pair<>(MapleStat.MP, Math.min(mp, maxmp))); - statup.add(new Pair<>(MapleStat.MAXHP, maxhp)); - statup.add(new Pair<>(MapleStat.MAXMP, maxmp)); - client.announce(MaplePacketCreator.updatePlayerStats(statup, this)); - } - if (effect.isMonsterRiding()) { - if (effect.getSourceId() != Corsair.BATTLE_SHIP) { - this.getClient().getWorldServer().unregisterMountHunger(this); - this.getMount().setActive(false); - } - } - if (!overwrite) { - cancelPlayerBuffs(buffstats); - } - - return toCancel; - } - - public void cancelEffectFromBuffStat(MapleBuffStat stat) { - MapleBuffStatValueHolder effect; - - chrLock.lock(); - try { - effect = effects.get(stat); - } finally { - chrLock.unlock(); - } - if (effect != null) { - cancelEffect(effect.effect, false, -1); - } - } - public void Hide(boolean hide, boolean login) { if (isGM() && hide != this.hidden) { if (!hide) { @@ -965,7 +825,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { announce(MaplePacketCreator.enableActions()); } } - + public void Hide(boolean hide) { Hide(hide, false); } @@ -975,18 +835,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public void cancelMagicDoor() { - List mbsvhList; - - chrLock.lock(); - try { - mbsvhList = new ArrayList<>(effects.values()); - } finally { - chrLock.unlock(); - } - + List mbsvhList = getAllStatups(); for (MapleBuffStatValueHolder mbsvh : mbsvhList) { if (mbsvh.effect.isMagicDoor()) { cancelEffect(mbsvh.effect, false, mbsvh.startTime); + break; } } } @@ -1321,6 +1174,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } private boolean buffMapProtection() { + effLock.lock(); chrLock.lock(); try { MapleMap thisMap = client.getChannelServer().getMapFactory().getMap(mapid); @@ -1336,6 +1190,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } finally { chrLock.unlock(); + effLock.unlock(); } for(Item it: this.getInventory(MapleInventoryType.EQUIPPED).list()) { @@ -1664,15 +1519,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { Skill battleship = SkillFactory.getSkill(Corsair.BATTLE_SHIP); int cooldown = battleship.getEffect(getSkillLevel(battleship)).getCooldown(); announce(MaplePacketCreator.skillCooldown(Corsair.BATTLE_SHIP, cooldown)); - addCooldown(Corsair.BATTLE_SHIP, System.currentTimeMillis(), cooldown, TimerManager.getInstance().schedule(new CancelCooldownAction(this, Corsair.BATTLE_SHIP), cooldown * 1000)); + addCooldown(Corsair.BATTLE_SHIP, System.currentTimeMillis(), (long)(cooldown * 1000)); removeCooldown(5221999); cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING); } else { announce(MaplePacketCreator.skillCooldown(5221999, battleshipHp / 10)); //:D - addCooldown(5221999, 0, battleshipHp, null); + addCooldown(5221999, 0, Long.MAX_VALUE); } } - + public void decreaseReports() { this.possibleReports--; } @@ -1918,86 +1773,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } - private void dropBuffStats(List effectsToCancel) { - for (MapleBuffStatValueHolder cancelEffectCancelTasks : effectsToCancel) { - if (cancelEffectCancelTasks.schedule != null) { - cancelEffectCancelTasks.schedule.cancel(false); - this.cancelEffect(cancelEffectCancelTasks.effect, false, -1, false); - } - } - } - - private List deregisterBuffStats(List stats) { - chrLock.lock(); - try { - List effectsToCancel = new ArrayList<>(stats.size()); - for (MapleBuffStat stat : stats) { - MapleBuffStatValueHolder mbsvh = effects.get(stat); - if (mbsvh != null) { - effects.remove(stat); - boolean addMbsvh = true; - for (MapleBuffStatValueHolder contained : effectsToCancel) { - if (mbsvh.startTime == contained.startTime && contained.effect == mbsvh.effect) { - addMbsvh = false; - } - } - if (addMbsvh) { - effectsToCancel.add(mbsvh); - } - if (stat == MapleBuffStat.RECOVERY) { - if (recoveryTask != null) { - recoveryTask.cancel(false); - recoveryTask = null; - } - } else if (stat == MapleBuffStat.SUMMON || stat == MapleBuffStat.PUPPET) { - int summonId = mbsvh.effect.getSourceId(); - - MapleSummon summon = summons.get(summonId); - if (summon != null) { - getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition()); - getMap().removeMapObject(summon); - removeVisibleMapObject(summon); - summons.remove(summonId); - - if (summon.getSkill() == DarkKnight.BEHOLDER) { - if (beholderHealingSchedule != null) { - beholderHealingSchedule.cancel(false); - beholderHealingSchedule = null; - } - if (beholderBuffSchedule != null) { - beholderBuffSchedule.cancel(false); - beholderBuffSchedule = null; - } - } - } - } else if (stat == MapleBuffStat.DRAGONBLOOD) { - dragonBloodSchedule.cancel(false); - dragonBloodSchedule = null; - } else if (stat == MapleBuffStat.HPREC || stat == MapleBuffStat.MPREC) { - if(stat == MapleBuffStat.HPREC) { - extraHpRec = 0; - } else { - extraMpRec = 0; - } - - if (extraRecoveryTask != null) { - extraRecoveryTask.cancel(false); - extraRecoveryTask = null; - } - - if(extraHpRec != 0 || extraMpRec != 0) { - startExtraTaskInternal(extraHpRec, extraMpRec, extraRecInterval); - } - } - } - } - - return effectsToCancel; - } finally { - chrLock.unlock(); - } - } - public void stopChairTask() { chrLock.lock(); try { @@ -2103,15 +1878,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public void dispel() { - List mbsvhList; - - chrLock.lock(); - try { - mbsvhList = new ArrayList<>(effects.values()); - } finally { - chrLock.unlock(); - } - + List mbsvhList = getAllStatups(); for (MapleBuffStatValueHolder mbsvh : mbsvhList) { if (mbsvh.effect.isSkill()) { cancelEffect(mbsvh.effect, false, mbsvh.startTime); @@ -2199,14 +1966,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public void dispelSkill(int skillid) { - LinkedList allBuffs; - chrLock.lock(); - try { - allBuffs = new LinkedList<>(effects.values()); - } finally { - chrLock.unlock(); - } - + List allBuffs = getAllStatups(); for (MapleBuffStatValueHolder mbsvh : allBuffs) { if (skillid == 0) { if (mbsvh.effect.isSkill() && (mbsvh.effect.getSourceId() % 10000000 == 1004 || dispelSkills(mbsvh.effect.getSourceId()))) { @@ -2313,17 +2073,88 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { Server.getInstance().getWorld(world).updateMessenger(getMessenger(), getName(), getWorld(), client.getChannel()); } } + + public void cancelBuffExpireTask() { + if (buffExpireTask != null) { + buffExpireTask.cancel(false); + buffExpireTask = null; + } + } + public void buffExpireTask() { + if (buffExpireTask == null) { + buffExpireTask = TimerManager.getInstance().register(new Runnable() { + @Override + public void run() { + Set> es; + + effLock.lock(); + chrLock.lock(); + try { + es = new LinkedHashSet<>(buffExpires.entrySet()); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + + long curTime = System.currentTimeMillis(); + for(Entry bel : es) { + if(curTime >= bel.getValue()) { + MapleBuffStatValueHolder mbsvh = buffEffects.get(bel.getKey()).entrySet().iterator().next().getValue(); // rofl + cancelEffect(mbsvh.effect, false, mbsvh.startTime); + } + } + } + }, 1500); + } + } + + public void cancelSkillCooldownTask() { + if (skillCooldownTask != null) { + skillCooldownTask.cancel(false); + skillCooldownTask = null; + } + } + + public void skillCooldownTask() { + if (skillCooldownTask == null) { + skillCooldownTask = TimerManager.getInstance().register(new Runnable() { + @Override + public void run() { + Set> es; + + effLock.lock(); + chrLock.lock(); + try { + es = new LinkedHashSet<>(coolDowns.entrySet()); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + + long curTime = System.currentTimeMillis(); + for(Entry bel : es) { + MapleCoolDownValueHolder mcdvh = bel.getValue(); + if(curTime >= mcdvh.startTime + mcdvh.length) { + removeCooldown(mcdvh.skillId); + client.announce(MaplePacketCreator.skillCooldown(mcdvh.skillId, 0)); + } + } + } + }, 1500); + } + } + public void cancelExpirationTask() { - if (expiretask != null) { - expiretask.cancel(false); - expiretask = null; + if (itemExpireTask != null) { + itemExpireTask.cancel(false); + itemExpireTask = null; } } public void expirationTask() { - if (expiretask == null) { - expiretask = TimerManager.getInstance().register(new Runnable() { + if (itemExpireTask == null) { + itemExpireTask = TimerManager.getInstance().register(new Runnable() { @Override public void run() { boolean deletedCoupon = false; @@ -2498,38 +2329,20 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return accountid; } - public List getAllBuffs() { - chrLock.lock(); - try { - List ret = new ArrayList<>(); - for (MapleBuffStatValueHolder mbsvh : effects.values()) { - ret.add(new PlayerBuffValueHolder(mbsvh.startTime, mbsvh.effect)); - } - return ret; - } finally { - chrLock.unlock(); - } - } - - public List> getAllStatups() { - chrLock.lock(); - try { - List> ret = new ArrayList<>(); - for (MapleBuffStat mbs : effects.keySet()) { - MapleBuffStatValueHolder mbsvh = effects.get(mbs); - ret.add(new Pair<>(mbs, mbsvh.value)); - } - return ret; - } finally { - chrLock.unlock(); - } - } - public List getAllCooldowns() { List ret = new ArrayList<>(); - for (MapleCoolDownValueHolder mcdvh : coolDowns.values()) { - ret.add(new PlayerCoolDownValueHolder(mcdvh.skillId, mcdvh.startTime, mcdvh.length)); + + effLock.lock(); + chrLock.lock(); + try { + for (MapleCoolDownValueHolder mcdvh : coolDowns.values()) { + ret.add(new PlayerCoolDownValueHolder(mcdvh.skillId, mcdvh.startTime, mcdvh.length)); + } + } finally { + chrLock.unlock(); + effLock.unlock(); } + return ret; } @@ -2587,6 +2400,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public Long getBuffedStarttime(MapleBuffStat effect) { + effLock.lock(); chrLock.lock(); try { MapleBuffStatValueHolder mbsvh = effects.get(effect); @@ -2596,10 +2410,12 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return Long.valueOf(mbsvh.startTime); } finally { chrLock.unlock(); + effLock.unlock(); } } public Integer getBuffedValue(MapleBuffStat effect) { + effLock.lock(); chrLock.lock(); try { MapleBuffStatValueHolder mbsvh = effects.get(effect); @@ -2609,10 +2425,12 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return Integer.valueOf(mbsvh.value); } finally { chrLock.unlock(); + effLock.unlock(); } } public int getBuffSource(MapleBuffStat stat) { + effLock.lock(); chrLock.lock(); try { MapleBuffStatValueHolder mbsvh = effects.get(stat); @@ -2622,10 +2440,12 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return mbsvh.effect.getSourceId(); } finally { chrLock.unlock(); + effLock.unlock(); } } public MapleStatEffect getBuffEffect(MapleBuffStat stat) { + effLock.lock(); chrLock.lock(); try { MapleBuffStatValueHolder mbsvh = effects.get(stat); @@ -2636,25 +2456,751 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } finally { chrLock.unlock(); + effLock.unlock(); } } - private List getBuffStats(MapleStatEffect effect, long startTime) { + public Set getAvailableBuffs() { + effLock.lock(); chrLock.lock(); try { - List stats = new ArrayList<>(); - for (Entry stateffect : effects.entrySet()) { - if(stateffect.getValue() == null) continue; - - if (stateffect.getValue().effect.sameSource(effect) && (startTime == -1 || startTime == stateffect.getValue().startTime)) { - stats.add(stateffect.getKey()); + return buffEffects.keySet(); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + } + + private List getAllStatups() { + effLock.lock(); + chrLock.lock(); + try { + List ret = new ArrayList<>(); + for(Map bel : buffEffects.values()) { + for(MapleBuffStatValueHolder mbsvh : bel.values()) { + ret.add(mbsvh); } } + return ret; + } finally { + chrLock.unlock(); + effLock.unlock(); + } + } + + public List getAllBuffs() { // buff values will be stored in an arbitrary order + effLock.lock(); + chrLock.lock(); + try { + Map ret = new LinkedHashMap<>(); + for(Map bel : buffEffects.values()) { + for(MapleBuffStatValueHolder mbsvh : bel.values()) { + int srcid = mbsvh.effect.getBuffSourceId(); + if(!ret.containsKey(srcid)) { + ret.put(srcid, new PlayerBuffValueHolder(mbsvh.startTime, mbsvh.effect)); + } + } + } + return new ArrayList<>(ret.values()); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + } + + public List> getAllActiveStatups() { + effLock.lock(); + chrLock.lock(); + try { + List> ret = new ArrayList<>(); + for (MapleBuffStat mbs : effects.keySet()) { + MapleBuffStatValueHolder mbsvh = effects.get(mbs); + ret.add(new Pair<>(mbs, mbsvh.value)); + } + return ret; + } finally { + chrLock.unlock(); + effLock.unlock(); + } + } + + private List> getActiveStatupsFromSourceid(int sourceid) { // already under effLock & chrLock + List> ret = new ArrayList<>(); + + for(Entry bel : buffEffects.get(sourceid).entrySet()) { + Integer bsrcid = bel.getValue().effect.getBuffSourceId(); + MapleBuffStat mbs = bel.getKey(); + + MapleBuffStatValueHolder mbsvh = effects.get(bel.getKey()); + if(mbsvh != null && mbsvh.effect.getBuffSourceId() == bsrcid) { + ret.add(new Pair<>(mbs, mbsvh.value)); + } else { + ret.add(new Pair<>(mbs, 0)); + } + } + + Collections.sort(ret, new Comparator>() { + @Override + public int compare(Pair p1, Pair p2) { + return p1.getLeft().compareTo(p2.getLeft()); + } + }); + + return ret; + } + + private void addItemEffectHolder(Integer sourceid, long expirationtime, Map statups) { + buffEffects.put(sourceid, statups); + buffExpires.put(sourceid, expirationtime); + } + + private void removeEffectFromItemEffectHolder(Integer sourceid, MapleBuffStat buffStat) { + Map lbe = buffEffects.get(sourceid); + + if(lbe.remove(buffStat) != null) { + buffEffectsCount.put(buffStat, (byte)(buffEffectsCount.get(buffStat) - 1)); + + if(lbe.isEmpty()) { + buffEffects.remove(sourceid); + buffExpires.remove(sourceid); + } + } + } + + private void removeItemEffectHolder(Integer sourceid) { + Map be = buffEffects.remove(sourceid); + if(be != null) { + for(Entry bei : be.entrySet()) { + buffEffectsCount.put(bei.getKey(), (byte)(buffEffectsCount.get(bei.getKey()) - 1)); + } + } + + buffExpires.remove(sourceid); + } + + private void dropWorstEffectFromItemEffectHolder(MapleBuffStat mbs) { + Integer min = Integer.MAX_VALUE; + Integer srcid = -1; + for(Entry> bpl: buffEffects.entrySet()) { + MapleBuffStatValueHolder mbsvh = bpl.getValue().get(mbs); + if(mbsvh != null) { + if(mbsvh.value < min) { + min = mbsvh.value; + srcid = bpl.getKey(); + } + } + } + + removeEffectFromItemEffectHolder(srcid, mbs); + } + + private MapleBuffStatValueHolder fetchBestEffectFromItemEffectHolder(MapleBuffStat mbs) { + Integer max = Integer.MIN_VALUE; + MapleBuffStatValueHolder mbsvh = null; + for(Entry> bpl: buffEffects.entrySet()) { + MapleBuffStatValueHolder mbsvhi = bpl.getValue().get(mbs); + if(mbsvhi != null) { + if(mbsvhi.value > max) { + max = mbsvhi.value; + mbsvh = mbsvhi; + } + } + } + + if(mbsvh != null) effects.put(mbs, mbsvh); + return mbsvh; + } + + private void extractBuffValue(int sourceid, MapleBuffStat stat) { + chrLock.lock(); + try { + if(buffEffects.get(sourceid).remove(stat) != null) { + buffEffectsCount.put(stat, (byte)(buffEffectsCount.get(stat) - 1)); + } + } finally { + chrLock.unlock(); + } + } + + private void debugListAllBuffs() { + effLock.lock(); + chrLock.lock(); + try { + System.out.println("-------------------"); + System.out.println("CACHED BUFFS: "); + for(Entry> bpl : buffEffects.entrySet()) { + System.out.print(bpl.getKey() + ": "); + for(Entry pble : bpl.getValue().entrySet()) { + System.out.print(pble.getKey().name() + pble.getValue().value + ", "); + } + System.out.println(); + } + System.out.println("-------------------"); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + } + + private void debugListAllBuffsCount() { + effLock.lock(); + chrLock.lock(); + try { + for(Entry mbsl : buffEffectsCount.entrySet()) { + System.out.println(mbsl.getKey().name() + " -> " + mbsl.getValue()); + } + } finally { + chrLock.unlock(); + effLock.unlock(); + } + } + + public void cancelAllBuffs(boolean disconnect) { + if (disconnect) { + effLock.lock(); + chrLock.lock(); + try { + effects.clear(); + + for(Integer srcid : new ArrayList<>(buffEffects.keySet())) { + removeItemEffectHolder(srcid); + } + } finally { + chrLock.unlock(); + effLock.unlock(); + } + } else { + List mbsvhList; + + effLock.lock(); + chrLock.lock(); + try { + List> mbls = new LinkedList<>(); + + for(Entry> bpl : buffEffects.entrySet()) { + for(Entry mbse : bpl.getValue().entrySet()) { + if(effects.get(mbse.getKey()) != mbse.getValue()) { + mbls.add(new Pair<>(bpl.getKey(), mbse.getKey())); + } + } + } + + for(Pair pmbs : mbls) { + removeEffectFromItemEffectHolder(pmbs.getLeft(), pmbs.getRight()); + } + + mbsvhList = new ArrayList<>(effects.values()); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + + for (MapleBuffStatValueHolder mbsvh : mbsvhList) { + cancelEffect(mbsvh.effect, false, mbsvh.startTime); + } + } + } + + private void dropBuffStats(List> effectsToCancel) { + for (Pair cancelEffectCancelTasks : effectsToCancel) { + //boolean nestedCancel = false; + + chrLock.lock(); + try { + /* + if (buffExpires.get(cancelEffectCancelTasks.getRight().effect.getBuffSourceId()) != null) { + nestedCancel = true; + }*/ + + if(cancelEffectCancelTasks.getRight().bestApplied) { + fetchBestEffectFromItemEffectHolder(cancelEffectCancelTasks.getLeft()); + } + } finally { + chrLock.unlock(); + } + + recalcLocalStats(); + + /* + if (nestedCancel) { + this.cancelEffect(cancelEffectCancelTasks.getRight().effect, false, -1, false); + }*/ + } + } + + private List> deregisterBuffStats(Map stats) { + chrLock.lock(); + try { + List> effectsToCancel = new ArrayList<>(stats.size()); + for (Entry stat : stats.entrySet()) { + int sourceid = stat.getValue().effect.getBuffSourceId(); + + if(buffEffects.get(sourceid) == null) { + buffExpires.remove(sourceid); + } + + MapleBuffStat mbs = stat.getKey(); + effectsToCancel.add(new Pair<>(mbs, stat.getValue())); + + MapleBuffStatValueHolder mbsvh = effects.get(mbs); + if (mbsvh != null && mbsvh.effect.getBuffSourceId() == sourceid) { + mbsvh.bestApplied = true; + effects.remove(mbs); + + if (mbs == MapleBuffStat.RECOVERY) { + if (recoveryTask != null) { + recoveryTask.cancel(false); + recoveryTask = null; + } + } else if (mbs == MapleBuffStat.SUMMON || mbs == MapleBuffStat.PUPPET) { + int summonId = mbsvh.effect.getSourceId(); + + MapleSummon summon = summons.get(summonId); + if (summon != null) { + getMap().broadcastMessage(MaplePacketCreator.removeSummon(summon, true), summon.getPosition()); + getMap().removeMapObject(summon); + removeVisibleMapObject(summon); + summons.remove(summonId); + + if (summon.getSkill() == DarkKnight.BEHOLDER) { + if (beholderHealingSchedule != null) { + beholderHealingSchedule.cancel(false); + beholderHealingSchedule = null; + } + if (beholderBuffSchedule != null) { + beholderBuffSchedule.cancel(false); + beholderBuffSchedule = null; + } + } + } + } else if (mbs == MapleBuffStat.DRAGONBLOOD) { + dragonBloodSchedule.cancel(false); + dragonBloodSchedule = null; + } else if (mbs == MapleBuffStat.HPREC || mbs == MapleBuffStat.MPREC) { + if(mbs == MapleBuffStat.HPREC) { + extraHpRec = 0; + } else { + extraMpRec = 0; + } + + if (extraRecoveryTask != null) { + extraRecoveryTask.cancel(false); + extraRecoveryTask = null; + } + + if(extraHpRec != 0 || extraMpRec != 0) { + startExtraTaskInternal(extraHpRec, extraMpRec, extraRecInterval); + } + } + } + } + + return effectsToCancel; + } finally { + chrLock.unlock(); + } + } + + public void cancelEffect(int itemId) { + cancelEffect(MapleItemInformationProvider.getInstance().getItemEffect(itemId), false, -1); + } + + public void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) { + effLock.lock(); + try { + cancelEffect(effect, overwrite, startTime, true); + } finally { + effLock.unlock(); + } + } + + private void updateEffects(Set removedStats) { + chrLock.lock(); + try { + Map> retrievedEffects = new LinkedHashMap<>(); + + for(Entry> bel : buffEffects.entrySet()) { + for(Entry belv : bel.getValue().entrySet()) { + if(removedStats.contains(belv.getKey())) { + retrievedEffects.put(bel.getKey(), new Pair<>(belv.getValue().effect, belv.getValue().startTime)); + } + } + } + + for(Entry> lmse: retrievedEffects.entrySet()) { + lmse.getValue().getLeft().updateBuffEffect(this, getActiveStatupsFromSourceid(lmse.getKey()), lmse.getValue().getRight()); + } + } finally { + chrLock.unlock(); + } + } + + private void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime, boolean firstCancel) { + Set removedStats = new LinkedHashSet<>(); + dropBuffStats(cancelEffectInternal(effect, overwrite, startTime, removedStats)); + updateEffects(removedStats); + } + + private List> cancelEffectInternal(MapleStatEffect effect, boolean overwrite, long startTime, Set removedStats) { + Map buffstats; + if (!overwrite) { // is removing the source effect, meaning every effect from this srcid is being purged + buffstats = extractCurrentBuffStats(effect); + } else { // is dropping ALL current statups that uses same stats as the given effect + buffstats = extractLeastRelevantStatEffectsIfFull(effect); + } + + if (effect.isMagicDoor()) { + MapleDoor destroyDoor; + + chrLock.lock(); + try { + destroyDoor = doors.remove(this.getId()); + } finally { + chrLock.unlock(); + } + + if (destroyDoor != null) { + destroyDoor.getTarget().removeMapObject(destroyDoor.getAreaDoor()); + destroyDoor.getTown().removeMapObject(destroyDoor.getTownDoor()); + + for (MapleCharacter chr : destroyDoor.getTarget().getCharacters()) { + destroyDoor.getAreaDoor().sendDestroyData(chr.getClient()); + } + for (MapleCharacter chr : destroyDoor.getTown().getCharacters()) { + destroyDoor.getTownDoor().sendDestroyData(chr.getClient()); + } + + if (party != null) { + for (MaplePartyCharacter partyMembers : getParty().getMembers()) { + partyMembers.getPlayer().removeDoor(this.getId()); + partyMembers.removeDoor(this.getId()); + } + silentPartyUpdate(); + } + } + } + + List> toCancel = deregisterBuffStats(buffstats); + if (effect.getSourceId() == Spearman.HYPER_BODY || effect.getSourceId() == GM.HYPER_BODY || effect.getSourceId() == SuperGM.HYPER_BODY) { + List> statup = new ArrayList<>(4); + statup.add(new Pair<>(MapleStat.HP, Math.min(hp, maxhp))); + statup.add(new Pair<>(MapleStat.MP, Math.min(mp, maxmp))); + statup.add(new Pair<>(MapleStat.MAXHP, maxhp)); + statup.add(new Pair<>(MapleStat.MAXMP, maxmp)); + client.announce(MaplePacketCreator.updatePlayerStats(statup, this)); + } + if (effect.isMonsterRiding()) { + if (effect.getSourceId() != Corsair.BATTLE_SHIP) { + this.getClient().getWorldServer().unregisterMountHunger(this); + this.getMount().setActive(false); + } + } + + if (!overwrite) { + List cancelStats = new LinkedList<>(); + + chrLock.lock(); + try { + for(Entry mbsl : buffstats.entrySet()) { + cancelStats.add(mbsl.getKey()); + } + } finally { + chrLock.unlock(); + } + + for(MapleBuffStat mbs : cancelStats) removedStats.add(mbs); + cancelPlayerBuffs(cancelStats); + } + + return toCancel; + } + + public void cancelEffectFromBuffStat(MapleBuffStat stat) { + MapleBuffStatValueHolder effect; + + effLock.lock(); + chrLock.lock(); + try { + effect = effects.get(stat); + } finally { + chrLock.unlock(); + effLock.unlock(); + } + if (effect != null) { + cancelEffect(effect.effect, false, -1); + } + } + + public void cancelBuffStats(MapleBuffStat stat) { + effLock.lock(); + try { + List> cancelList = new LinkedList<>(); + + chrLock.lock(); + try { + for(Entry> bel : this.buffEffects.entrySet()) { + MapleBuffStatValueHolder beli = bel.getValue().get(stat); + if(beli != null) { + cancelList.add(new Pair<>(bel.getKey(), beli)); + } + } + } finally { + chrLock.unlock(); + } + + Map buffStatList = new LinkedHashMap<>(); + for(Pair p : cancelList) { + buffStatList.put(stat, p.getRight()); + extractBuffValue(p.getLeft(), stat); + dropBuffStats(deregisterBuffStats(buffStatList)); + } + } finally { + effLock.unlock(); + } + + cancelPlayerBuffs(Arrays.asList(stat)); + } + + private Map extractCurrentBuffStats(MapleStatEffect effect) { + chrLock.lock(); + try { + Map stats = new LinkedHashMap<>(); + Map buffList = buffEffects.remove(effect.getBuffSourceId()); + for (Entry stateffect : buffList.entrySet()) { + stats.put(stateffect.getKey(), stateffect.getValue()); + buffEffectsCount.put(stateffect.getKey(), (byte)(buffEffectsCount.get(stateffect.getKey()) - 1)); + } + return stats; } finally { chrLock.unlock(); } } + + private Map extractLeastRelevantStatEffectsIfFull(MapleStatEffect effect) { + Map extractedStatBuffs = new LinkedHashMap<>(); + + chrLock.lock(); + try { + Map stats = new LinkedHashMap<>(); + Map minStatBuffs = new LinkedHashMap<>(); + + for(Entry> mbsvhi : buffEffects.entrySet()) { + for(Entry mbsvh : mbsvhi.getValue().entrySet()) { + MapleBuffStat mbs = mbsvh.getKey(); + Byte it = stats.get(mbs); + + if(it != null) { + stats.put(mbs, (byte) (it + 1)); + if(mbsvh.getValue().value < minStatBuffs.get(mbs).value) minStatBuffs.put(mbs, mbsvh.getValue()); + } else { + stats.put(mbs, (byte) 1); + minStatBuffs.put(mbs, mbsvh.getValue()); + } + } + } + + Set effectStatups = new LinkedHashSet<>(); + for(Pair efstat : effect.getStatups()) { + effectStatups.add(efstat.getLeft()); + } + + for(Entry it : stats.entrySet()) { + boolean uniqueBuff = isSingletonStatup(it.getKey()); + + if(it.getValue() >= (!uniqueBuff ? ServerConstants.MAX_MONITORED_BUFFSTATS : 1) && effectStatups.contains(it.getKey())) { + MapleBuffStatValueHolder mbsvh = minStatBuffs.get(it.getKey()); + + Map lpbe = buffEffects.get(mbsvh.effect.getBuffSourceId()); + lpbe.remove(it.getKey()); + buffEffectsCount.put(it.getKey(), (byte)(buffEffectsCount.get(it.getKey()) - 1)); + + if(lpbe.isEmpty()) buffEffects.remove(mbsvh.effect.getBuffSourceId()); + extractedStatBuffs.put(it.getKey(), mbsvh); + } + } + } finally { + chrLock.unlock(); + } + + return extractedStatBuffs; + } + + private boolean isSingletonStatup(MapleBuffStat mbs) { + switch(mbs) { + case RECOVERY: + case HPREC: + case MPREC: + case SUMMON: + case PUPPET: + case DRAGONBLOOD: + case MONSTER_RIDING: + case MORPH: + case HYPERBODYHP: + case HYPERBODYMP: + return true; + + default: + return false; + } + } + + public void registerEffect(MapleStatEffect effect, long starttime, long expirationtime, boolean isSilent) { + if (effect.isDragonBlood()) { + prepareDragonBlood(effect); + } else if (effect.isBerserk()) { + checkBerserk(isHidden()); + } else if (effect.isBeholder()) { + final int beholder = DarkKnight.BEHOLDER; + if (beholderHealingSchedule != null) { + beholderHealingSchedule.cancel(false); + } + if (beholderBuffSchedule != null) { + beholderBuffSchedule.cancel(false); + } + Skill bHealing = SkillFactory.getSkill(DarkKnight.AURA_OF_BEHOLDER); + int bHealingLvl = getSkillLevel(bHealing); + if (bHealingLvl > 0) { + final MapleStatEffect healEffect = bHealing.getEffect(bHealingLvl); + int healInterval = healEffect.getX() * 1000; + beholderHealingSchedule = TimerManager.getInstance().register(new Runnable() { + @Override + public void run() { + addHP(healEffect.getHp()); + client.announce(MaplePacketCreator.showOwnBuffEffect(beholder, 2)); + getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.summonSkill(getId(), beholder, 5), true); + getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showOwnBuffEffect(beholder, 2), false); + } + }, healInterval, healInterval); + } + Skill bBuff = SkillFactory.getSkill(DarkKnight.HEX_OF_BEHOLDER); + if (getSkillLevel(bBuff) > 0) { + final MapleStatEffect buffEffect = bBuff.getEffect(getSkillLevel(bBuff)); + int buffInterval = buffEffect.getX() * 1000; + beholderBuffSchedule = TimerManager.getInstance().register(new Runnable() { + @Override + public void run() { + buffEffect.applyTo(MapleCharacter.this); + client.announce(MaplePacketCreator.showOwnBuffEffect(beholder, 2)); + getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.summonSkill(getId(), beholder, (int) (Math.random() * 3) + 6), true); + getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showBuffeffect(getId(), beholder, 2), false); + } + }, buffInterval, buffInterval); + } + } else if (effect.isRecovery()) { + int healInterval = (ServerConstants.USE_ULTRA_RECOVERY) ? 2000 : 5000; + final byte heal = (byte) effect.getX(); + + chrLock.lock(); + try { + if(recoveryTask != null) { + recoveryTask.cancel(false); + } + + recoveryTask = TimerManager.getInstance().register(new Runnable() { + @Override + public void run() { + if (getBuffSource(MapleBuffStat.RECOVERY) == -1) { + chrLock.lock(); + try { + if (recoveryTask != null) { + recoveryTask.cancel(false); + recoveryTask = null; + } + } finally { + chrLock.unlock(); + } + + return; + } + + addHP(heal); + client.announce(MaplePacketCreator.showOwnRecovery(heal)); + getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showRecovery(id, heal), false); + } + }, healInterval, healInterval); + } finally { + chrLock.unlock(); + } + } else if (effect.isDojoBuff() || effect.getSourceId() == 2022337) { + boolean isRecoveryBuff = false; + if(effect.getHpRRate() > 0) { + extraHpRec = effect.getHpR(); + extraRecInterval = effect.getHpRRate(); + isRecoveryBuff = true; + } + + if(effect.getMpRRate() > 0) { + extraMpRec = effect.getMpR(); + extraRecInterval = effect.getMpRRate(); + isRecoveryBuff = true; + } + + if(isRecoveryBuff) { + stopExtraTask(); + startExtraTask(extraHpRec, extraMpRec, extraRecInterval); // HP & MP sharing the same task holder + } + } + + effLock.lock(); + chrLock.lock(); + try { + Integer sourceid = effect.getBuffSourceId(); + Map toDeploy; + Map appliedStatups = new LinkedHashMap<>(); + + for(Pair ps : effect.getStatups()) { + appliedStatups.put(ps.getLeft(), new MapleBuffStatValueHolder(effect, starttime, ps.getRight())); + } + + if(ServerConstants.USE_BUFF_MOST_SIGNIFICANT) { + toDeploy = new LinkedHashMap<>(); + Map> retrievedEffects = new LinkedHashMap<>(); + + for (Entry statup : appliedStatups.entrySet()) { + MapleBuffStatValueHolder mbsvh = effects.get(statup.getKey()); + if(mbsvh == null || mbsvh.value <= statup.getValue().value) { + toDeploy.put(statup.getKey(), statup.getValue()); + } else { + retrievedEffects.put(mbsvh.effect.getBuffSourceId(), new Pair<>(mbsvh.effect, mbsvh.startTime)); + } + + Byte val = buffEffectsCount.get(statup.getKey()); + if(val != null) val = (byte)(val + 1); + else val = (byte) 1; + + buffEffectsCount.put(statup.getKey(), val); + } + + if(!isSilent) { + for(Entry> lmse: retrievedEffects.entrySet()) { + lmse.getValue().getLeft().updateBuffEffect(this, getActiveStatupsFromSourceid(lmse.getKey()), lmse.getValue().getRight()); + } + } + } else { + for (Entry statup : appliedStatups.entrySet()) { + Byte val = buffEffectsCount.get(statup.getKey()); + if(val != null) val = (byte)(val + 1); + else val = (byte) 1; + + buffEffectsCount.put(statup.getKey(), val); + } + + toDeploy = appliedStatups; + } + + addItemEffectHolder(sourceid, expirationtime, appliedStatups); + + for (Entry statup : toDeploy.entrySet()) { + if(statup.getValue().value != 0) effects.put(statup.getKey(), statup.getValue()); + } + } finally { + chrLock.unlock(); + effLock.unlock(); + } + + recalcLocalStats(); + } public int getChair() { return chair.get(); @@ -3691,6 +4237,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public MapleStatEffect getStatForBuff(MapleBuffStat effect) { + effLock.lock(); chrLock.lock(); try { MapleBuffStatValueHolder mbsvh = effects.get(effect); @@ -3700,6 +4247,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return mbsvh.effect; } finally { chrLock.unlock(); + effLock.unlock(); } } @@ -3778,13 +4326,13 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { public void giveCoolDowns(final int skillid, long starttime, long length) { if (skillid == 5221999) { this.battleshipHp = (int) length; - addCooldown(skillid, 0, length, null); + addCooldown(skillid, 0, length); } else { int time = (int) ((length + starttime) - System.currentTimeMillis()); - addCooldown(skillid, System.currentTimeMillis(), time, TimerManager.getInstance().schedule(new CancelCooldownAction(this, skillid), time)); + addCooldown(skillid, System.currentTimeMillis(), time); } } - + public int gmLevel() { return gmLevel; } @@ -3919,11 +4467,13 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { public boolean isActiveBuffedValue(int skillid) { LinkedList allBuffs; + effLock.lock(); chrLock.lock(); try { allBuffs = new LinkedList<>(effects.values()); } finally { chrLock.unlock(); + effLock.unlock(); } for (MapleBuffStatValueHolder mbsvh : allBuffs) { @@ -3939,6 +4489,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public boolean isBuffFrom(MapleBuffStat stat, Skill skill) { + effLock.lock(); chrLock.lock(); try { MapleBuffStatValueHolder mbsvh = effects.get(stat); @@ -3948,6 +4499,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return mbsvh.effect.isSkill() && mbsvh.effect.getSourceId() == skill.getId(); } finally { chrLock.unlock(); + effLock.unlock(); } } @@ -4395,14 +4947,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public void dispelBuffCoupons() { - LinkedList allBuffs; - - chrLock.lock(); - try { - allBuffs = new LinkedList<>(effects.values()); - } finally { - chrLock.unlock(); - } + List allBuffs = getAllStatups(); for (MapleBuffStatValueHolder mbsvh : allBuffs) { if (ItemConstants.isRateCoupon(mbsvh.effect.getSourceId())) { @@ -4806,29 +5351,26 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { public MapleStatEffect effect; public long startTime; public int value; - public ScheduledFuture schedule; + public boolean bestApplied; - public MapleBuffStatValueHolder(MapleStatEffect effect, long startTime, ScheduledFuture schedule, int value) { + public MapleBuffStatValueHolder(MapleStatEffect effect, long startTime, int value) { super(); this.effect = effect; this.startTime = startTime; - this.schedule = schedule; this.value = value; + this.bestApplied = false; } } public static class MapleCoolDownValueHolder { - public int skillId; public long startTime, length; - public ScheduledFuture timer; - public MapleCoolDownValueHolder(int skillId, long startTime, long length, ScheduledFuture timer) { + public MapleCoolDownValueHolder(int skillId, long startTime, long length) { super(); this.skillId = skillId; this.startTime = startTime; this.length = length; - this.timer = timer; } } @@ -5147,119 +5689,22 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } - public void registerEffect(MapleStatEffect effect, long starttime, ScheduledFuture schedule) { - if (effect.isDragonBlood()) { - prepareDragonBlood(effect); - } else if (effect.isBerserk()) { - checkBerserk(isHidden()); - } else if (effect.isBeholder()) { - final int beholder = DarkKnight.BEHOLDER; - if (beholderHealingSchedule != null) { - beholderHealingSchedule.cancel(false); - } - if (beholderBuffSchedule != null) { - beholderBuffSchedule.cancel(false); - } - Skill bHealing = SkillFactory.getSkill(DarkKnight.AURA_OF_BEHOLDER); - int bHealingLvl = getSkillLevel(bHealing); - if (bHealingLvl > 0) { - final MapleStatEffect healEffect = bHealing.getEffect(bHealingLvl); - int healInterval = healEffect.getX() * 1000; - beholderHealingSchedule = TimerManager.getInstance().register(new Runnable() { - @Override - public void run() { - addHP(healEffect.getHp()); - client.announce(MaplePacketCreator.showOwnBuffEffect(beholder, 2)); - getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.summonSkill(getId(), beholder, 5), true); - getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showOwnBuffEffect(beholder, 2), false); - } - }, healInterval, healInterval); - } - Skill bBuff = SkillFactory.getSkill(DarkKnight.HEX_OF_BEHOLDER); - if (getSkillLevel(bBuff) > 0) { - final MapleStatEffect buffEffect = bBuff.getEffect(getSkillLevel(bBuff)); - int buffInterval = buffEffect.getX() * 1000; - beholderBuffSchedule = TimerManager.getInstance().register(new Runnable() { - @Override - public void run() { - buffEffect.applyTo(MapleCharacter.this); - client.announce(MaplePacketCreator.showOwnBuffEffect(beholder, 2)); - getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.summonSkill(getId(), beholder, (int) (Math.random() * 3) + 6), true); - getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showBuffeffect(getId(), beholder, 2), false); - } - }, buffInterval, buffInterval); - } - } else if (effect.isRecovery()) { - int healInterval = (ServerConstants.USE_ULTRA_RECOVERY) ? 2000 : 5000; - final byte heal = (byte) effect.getX(); - - chrLock.lock(); - try { - recoveryTask = TimerManager.getInstance().register(new Runnable() { - @Override - public void run() { - if (getBuffSource(MapleBuffStat.RECOVERY) == -1) { - chrLock.lock(); - try { - if (recoveryTask != null) { - recoveryTask.cancel(false); - recoveryTask = null; - } - } finally { - chrLock.unlock(); - } - - return; - } - - addHP(heal); - client.announce(MaplePacketCreator.showOwnRecovery(heal)); - getMap().broadcastMessage(MapleCharacter.this, MaplePacketCreator.showRecovery(id, heal), false); - } - }, healInterval, healInterval); - } finally { - chrLock.unlock(); - } - } else if (effect.isDojoBuff() || effect.getSourceId() == 2022337) { - boolean isRecoveryBuff = false; - if(effect.getHpRRate() > 0) { - extraHpRec = effect.getHpR(); - extraRecInterval = effect.getHpRRate(); - isRecoveryBuff = true; - } - - if(effect.getMpRRate() > 0) { - extraMpRec = effect.getMpR(); - extraRecInterval = effect.getMpRRate(); - isRecoveryBuff = true; - } - - if(isRecoveryBuff) { - stopExtraTask(); - startExtraTask(extraHpRec, extraMpRec, extraRecInterval); // HP & MP sharing the same task holder - } - } - + public void removeAllCooldownsExcept(int id, boolean packet) { + effLock.lock(); chrLock.lock(); try { - for (Pair statup : effect.getStatups()) { - effects.put(statup.getLeft(), new MapleBuffStatValueHolder(effect, starttime, schedule, statup.getRight())); + ArrayList list = new ArrayList<>(coolDowns.values()); + for (MapleCoolDownValueHolder mcvh : list) { + if (mcvh.skillId != id) { + coolDowns.remove(mcvh.skillId); + if (packet) { + client.announce(MaplePacketCreator.skillCooldown(mcvh.skillId, 0)); + } + } } } finally { chrLock.unlock(); - } - - recalcLocalStats(); - } - - public void removeAllCooldownsExcept(int id, boolean packet) { - for (MapleCoolDownValueHolder mcvh : Collections.unmodifiableCollection(coolDowns.values())) { - if (mcvh.skillId != id) { - coolDowns.remove(mcvh.skillId); - if (packet) { - client.announce(MaplePacketCreator.skillCooldown(mcvh.skillId, 0)); - } - } + effLock.unlock(); } } @@ -5269,8 +5714,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public void removeCooldown(int skillId) { - if (this.coolDowns.containsKey(skillId)) { - this.coolDowns.remove(skillId); + effLock.lock(); + chrLock.lock(); + try { + if (this.coolDowns.containsKey(skillId)) { + this.coolDowns.remove(skillId); + } + } finally { + chrLock.unlock(); + effLock.unlock(); } } @@ -5381,13 +5833,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public synchronized void saveCooldowns() { - if (getAllCooldowns().size() > 0) { + List listcd = getAllCooldowns(); + + if (listcd.size() > 0) { try { Connection con = DatabaseConnection.getConnection(); deleteWhereCharacterId(con, "DELETE FROM cooldowns WHERE charid = ?"); try (PreparedStatement ps = con.prepareStatement("INSERT INTO cooldowns (charid, SkillID, StartTime, length) VALUES (?, ?, ?, ?)")) { ps.setInt(1, getId()); - for (PlayerCoolDownValueHolder cooling : getAllCooldowns()) { + for (PlayerCoolDownValueHolder cooling : listcd) { ps.setInt(2, cooling.skillId); ps.setLong(3, cooling.startTime); ps.setLong(4, cooling.length); @@ -5931,6 +6385,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public void setBuffedValue(MapleBuffStat effect, int value) { + effLock.lock(); chrLock.lock(); try { MapleBuffStatValueHolder mbsvh = effects.get(effect); @@ -5940,6 +6395,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { mbsvh.value = value; } finally { chrLock.unlock(); + effLock.unlock(); } } @@ -6497,7 +6953,14 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public boolean skillIsCooling(int skillId) { - return coolDowns.containsKey(Integer.valueOf(skillId)); + effLock.lock(); + chrLock.lock(); + try { + return coolDowns.containsKey(Integer.valueOf(skillId)); + } finally { + chrLock.unlock(); + effLock.unlock(); + } } public void runFullnessSchedule(int petSlot) { @@ -7096,6 +7559,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { if (extraRecoveryTask != null) { extraRecoveryTask.cancel(false); } + + cancelBuffExpireTask(); + cancelSkillCooldownTask(); cancelExpirationTask(); for (ScheduledFuture sf : timers) { sf.cancel(false); diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index 9a2c6c12da..be571003a5 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -1307,7 +1307,9 @@ public class MapleClient { } } server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs()); - player.cancelBuffEffects(); + player.cancelAllBuffs(true); + player.cancelBuffExpireTask(); + player.cancelSkillCooldownTask(); //Cancelling magicdoor? Nope //Cancelling mounts? Noty if (player.getBuffedValue(MapleBuffStat.PUPPET) != null) { diff --git a/src/client/command/Commands.java b/src/client/command/Commands.java index 740a1c40b8..b61d27fd98 100644 --- a/src/client/command/Commands.java +++ b/src/client/command/Commands.java @@ -1252,9 +1252,12 @@ public class Commands { } } - if(player.getJob().isA(MapleJob.ARAN1)) { + if(player.getJob().isA(MapleJob.ARAN1) || player.getJob().isA(MapleJob.LEGEND)) { skill = SkillFactory.getSkill(5001005); player.changeSkillLevel(skill, (byte) -1, -1, -1); + } else { + skill = SkillFactory.getSkill(21001001); + player.changeSkillLevel(skill, (byte) -1, -1, -1); } player.yellowMessage("Skills maxed out."); @@ -1664,7 +1667,7 @@ public class Commands { case "givems": if (sub.length < 3){ - player.yellowMessage("Syntax: !givems "); + player.yellowMessage("Syntax: !givems "); break; } @@ -2051,7 +2054,7 @@ public class Commands { try { if (sub.length == 2) { int itemId = Integer.parseInt(sub[1]); - if(!(itemId >= 30000 && itemId < 32000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { + if(!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { player.yellowMessage("Hair id '" + sub[1] + "' does not exist."); break; } @@ -2061,7 +2064,7 @@ public class Commands { player.equipChanged(); } else { int itemId = Integer.parseInt(sub[2]); - if(!(itemId >= 30000 && itemId < 32000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { + if(!(itemId >= 30000 && itemId < 35000) || MapleItemInformationProvider.getInstance().getName(itemId) == null) { player.yellowMessage("Hair id '" + sub[2] + "' does not exist."); break; } @@ -2278,6 +2281,7 @@ public class Commands { player.getMap().spawnMonsterOnGroundBelow(monster, player.getPosition()); break; + /* case "playernpc": if (sub.length < 3){ player.yellowMessage("Syntax: !playernpc "); @@ -2285,6 +2289,7 @@ public class Commands { } player.playerNPC(c.getChannelServer().getPlayerStorage().getCharacterByName(sub[1]), Integer.parseInt(sub[2])); break; + */ default: return false; diff --git a/src/client/inventory/Equip.java b/src/client/inventory/Equip.java index e4e824b946..c1027d514a 100644 --- a/src/client/inventory/Equip.java +++ b/src/client/inventory/Equip.java @@ -487,11 +487,11 @@ public class Equip extends Item { // from level 1 to 2 is killing about 100~200 mobs of the same level range, on a 1x EXP rate scenario. if(reqLevel >= 78) { - return Math.max(ServerConstants.EQUIP_EXPERIENCE_MOD * (10413.648 * Math.exp(reqLevel * 0.03275)), 15); + return Math.max(ServerConstants.EQUIP_EXP_RATE * (10413.648 * Math.exp(reqLevel * 0.03275)), 15); } else if(reqLevel >= 38) { - return Math.max(ServerConstants.EQUIP_EXPERIENCE_MOD * ( 4985.818 * Math.exp(reqLevel * 0.02007)), 15); + return Math.max(ServerConstants.EQUIP_EXP_RATE * ( 4985.818 * Math.exp(reqLevel * 0.02007)), 15); } else { - return Math.max(ServerConstants.EQUIP_EXPERIENCE_MOD * ( 248.219 * Math.exp(reqLevel * 0.11093)), 15); + return Math.max(ServerConstants.EQUIP_EXP_RATE * ( 248.219 * Math.exp(reqLevel * 0.11093)), 15); } } diff --git a/src/constants/ItemConstants.java b/src/constants/ItemConstants.java index 11cbff3ade..80347f53cf 100644 --- a/src/constants/ItemConstants.java +++ b/src/constants/ItemConstants.java @@ -36,8 +36,6 @@ public final class ItemConstants { public final static int KARMA = 0x10; public final static int PET_COME = 0x80; public final static int ACCOUNT_SHARING = 0x100; - public final static float ITEM_ARMOR_EXP = 1 / 350000; - public static final float ITEM_WEAPON_EXP = 1 / 700000; public final static boolean EXPIRING_ITEMS = true; diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index 4e35cea56e..bd24572cbf 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -33,7 +33,7 @@ public class ServerConstants { //Server Flags public static final boolean USE_CUSTOM_KEYSET = true; //Enables auto-setup of the MapleSolaxiaV2's custom keybindings when creating characters. public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true; - public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events of a map, rather than those of only view range. + public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range. public static final boolean USE_DEBUG = false; //Will enable some text prints on the client, oriented for debugging purposes. public static final boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids. public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info. @@ -45,20 +45,23 @@ public class ServerConstants { public static final boolean USE_PARTY_SEARCH = false; public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically. public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour). - public static final boolean USE_SERVER_AUTOASSIGNER = true; //Server-builtin autoassigner, uses algorithm based on distributing AP accordingly to required secondary stat on equipments. + public static final boolean USE_SERVER_AUTOASSIGNER = true; //Server-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments. public static final boolean USE_REFRESH_RANK_MOVE = true; public static final boolean USE_ENFORCE_MDOOR_POSITION = true; //Forces mystic door to be spawned near spawnpoints. (since things bugs out other way, and this helps players to locate the door faster) public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped. + public static final boolean USE_BUFF_MOST_SIGNIFICANT = true; //When applying buffs, the player will stick with the highest stat boost among the listed, rather than overwriting stats. //Server Rates And Experience public static final int EXP_RATE = 10; public static final int MESO_RATE = 10; public static final int DROP_RATE = 10; public static final int BOSS_DROP_RATE = 20; - public static final int PARTY_EXPERIENCE_MOD = 1; //Change for event stuff. - public static final double EQUIP_EXPERIENCE_MOD = 10.0; //Rate for equipment exp needed, grows linearly. Set 1.0 for default (about 100~200 same-level range mobs killed to pass equip from level 1 to 2). + public static final double EQUIP_EXP_RATE = 10.0; //Rate for equipment exp gain, grows linearly. Set 1.0 for default (about 100~200 same-level range mobs killed to pass equip from level 1 to 2). + + public static final int PARTY_EXPERIENCE_MOD = 1; //Change for event stuff. public static final double PQ_BONUS_EXP_MOD = 0.5; + public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires. public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner. public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system. public static final long BLOCK_NPC_RACE_CONDT = (long)(0.5 * 1000); //Time the player client must wait before reopening a conversation with an NPC. @@ -91,7 +94,7 @@ public class ServerConstants { //Equipment Configuration public static final boolean USE_EQUIPMNT_LVLUP_SLOTS = true;//Equips can upgrade slots at level up. - public static final boolean USE_EQUIPMNT_LVLUP_POWER = true;//Enable more powerful stats upgrades at equip level up. + public static final boolean USE_EQUIPMNT_LVLUP_POWER = true;//Enable more powerful stat upgrades at equip level up. public static final boolean USE_SPIKES_AVOID_BANISH = true; //Shoes equipped with spikes prevents mobs from banishing wearer. public static final boolean USE_CHAIR_EXTRAHEAL = true; //Enable map chairs to further recover player's HP and MP. public static final int MAX_EQUIPMNT_LVLUP_STAT_UP = 10000; //Max stat upgrade an equipment can have on a levelup. diff --git a/src/net/server/Server.java b/src/net/server/Server.java index ec92d13577..2653260ef0 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -21,6 +21,8 @@ */ package net.server; +import net.server.worker.CouponWorker; +import net.server.worker.RankingWorker; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; diff --git a/src/net/server/channel/handlers/CancelBuffHandler.java b/src/net/server/channel/handlers/CancelBuffHandler.java index 12f071eee4..9671895cf7 100644 --- a/src/net/server/channel/handlers/CancelBuffHandler.java +++ b/src/net/server/channel/handlers/CancelBuffHandler.java @@ -41,6 +41,8 @@ public final class CancelBuffHandler extends AbstractMaplePacketHandler implemen @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { int sourceid = slea.readInt(); + if(sourceid < 0) sourceid = -sourceid; //oh my... + switch (sourceid) { case FPArchMage.BIG_BANG: case ILArchMage.BIG_BANG: diff --git a/src/net/server/channel/handlers/CloseRangeDamageHandler.java b/src/net/server/channel/handlers/CloseRangeDamageHandler.java index 1e0fa93e53..37bd7929d9 100644 --- a/src/net/server/channel/handlers/CloseRangeDamageHandler.java +++ b/src/net/server/channel/handlers/CloseRangeDamageHandler.java @@ -32,7 +32,6 @@ import tools.Pair; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleBuffStat; import client.MapleCharacter; -import client.MapleCharacter.CancelCooldownAction; import client.MapleClient; import client.MapleJob; import client.MapleStat; @@ -175,7 +174,7 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler { return; } else { c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect_.getCooldown())); - player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000, TimerManager.getInstance().schedule(new CancelCooldownAction(player, attack.skill), effect_.getCooldown() * 1000)); + player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000); } } } diff --git a/src/net/server/channel/handlers/EnterCashShopHandler.java b/src/net/server/channel/handlers/EnterCashShopHandler.java index d2d6ecd63e..5af0f742a3 100644 --- a/src/net/server/channel/handlers/EnterCashShopHandler.java +++ b/src/net/server/channel/handlers/EnterCashShopHandler.java @@ -50,7 +50,9 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler { mc.closePlayerInteractions(); Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs()); - mc.cancelBuffEffects(); + mc.cancelAllBuffs(true); + mc.cancelBuffExpireTask(); + mc.cancelSkillCooldownTask(); mc.cancelExpirationTask(); c.announce(MaplePacketCreator.openCashShop(c, false)); diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java index 8629ed3747..ef2767e56e 100644 --- a/src/net/server/channel/handlers/EnterMTSHandler.java +++ b/src/net/server/channel/handlers/EnterMTSHandler.java @@ -59,6 +59,9 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { } Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs()); + chr.cancelAllBuffs(true); + chr.cancelBuffExpireTask(); + chr.cancelSkillCooldownTask(); chr.cancelExpirationTask(); chr.saveToDB(); chr.getMap().removePlayer(c.getPlayer()); diff --git a/src/net/server/channel/handlers/MagicDamageHandler.java b/src/net/server/channel/handlers/MagicDamageHandler.java index 7a6a51d33a..fb73ac7eca 100644 --- a/src/net/server/channel/handlers/MagicDamageHandler.java +++ b/src/net/server/channel/handlers/MagicDamageHandler.java @@ -27,7 +27,6 @@ import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleBuffStat; import client.MapleCharacter; -import client.MapleCharacter.CancelCooldownAction; import client.MapleClient; import client.Skill; import client.SkillFactory; @@ -76,7 +75,7 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler { return; } else { c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect_.getCooldown())); - player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000, TimerManager.getInstance().schedule(new CancelCooldownAction(player, attack.skill), effect_.getCooldown() * 1000)); + player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000); } } applyAttack(attack, player, effect.getAttackCount()); diff --git a/src/net/server/channel/handlers/PlayerInteractionHandler.java b/src/net/server/channel/handlers/PlayerInteractionHandler.java index b663e11034..8d60569f9b 100644 --- a/src/net/server/channel/handlers/PlayerInteractionHandler.java +++ b/src/net/server/channel/handlers/PlayerInteractionHandler.java @@ -32,6 +32,7 @@ import constants.ItemConstants; import java.util.Arrays; import net.AbstractMaplePacketHandler; +import net.server.Server; import server.MapleInventoryManipulator; import server.MapleItemInformationProvider; import server.MapleMiniGame; @@ -172,6 +173,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler { } else { HiredMerchant merchant = new HiredMerchant(chr, itemId, desc); chr.setHiredMerchant(merchant); + c.getWorldServer().registerHiredMerchant(merchant); chr.getClient().getChannelServer().addHiredMerchant(chr.getId(), merchant); chr.announce(MaplePacketCreator.getHiredMerchant(chr, merchant, true)); } diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java index 5a90e0efd7..b82e96612d 100644 --- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -256,6 +256,8 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.enableReport()); player.changeSkillLevel(SkillFactory.getSkill(10000000 * player.getJobType() + 12), (byte) (player.getLinkedLevel() / 10), 20, -1); player.checkBerserk(player.isHidden()); + player.buffExpireTask(); + player.skillCooldownTask(); player.expirationTask(); if (GameConstants.hasSPTable(player.getJob()) && player.getJob().getId() != 2001) { player.createDragon(); diff --git a/src/net/server/channel/handlers/RangedAttackHandler.java b/src/net/server/channel/handlers/RangedAttackHandler.java index f931d6fa40..cf255c8327 100644 --- a/src/net/server/channel/handlers/RangedAttackHandler.java +++ b/src/net/server/channel/handlers/RangedAttackHandler.java @@ -30,7 +30,6 @@ import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleBuffStat; import client.MapleCharacter; -import client.MapleCharacter.CancelCooldownAction; import client.MapleClient; import client.Skill; import client.SkillFactory; @@ -208,7 +207,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler { return; } else { c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect_.getCooldown())); - player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000, TimerManager.getInstance().schedule(new CancelCooldownAction(player, attack.skill), effect_.getCooldown() * 1000)); + player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000); } } } diff --git a/src/net/server/channel/handlers/SpecialMoveHandler.java b/src/net/server/channel/handlers/SpecialMoveHandler.java index 537f05b2aa..68f6b8ee7c 100644 --- a/src/net/server/channel/handlers/SpecialMoveHandler.java +++ b/src/net/server/channel/handlers/SpecialMoveHandler.java @@ -32,7 +32,6 @@ import tools.FilePrinter; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; import client.MapleCharacter; -import client.MapleCharacter.CancelCooldownAction; import client.autoban.AutobanFactory; import client.MapleClient; import client.MapleStat; @@ -84,8 +83,7 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler { return; } else if (skillid != Corsair.BATTLE_SHIP) { c.announce(MaplePacketCreator.skillCooldown(skillid, effect.getCooldown())); - ScheduledFuture timer = TimerManager.getInstance().schedule(new CancelCooldownAction(c.getPlayer(), skillid), effect.getCooldown() * 1000); - chr.addCooldown(skillid, System.currentTimeMillis(), effect.getCooldown() * 1000, timer); + chr.addCooldown(skillid, System.currentTimeMillis(), effect.getCooldown() * 1000); } } if (skillid == Hero.MONSTER_MAGNET || skillid == Paladin.MONSTER_MAGNET || skillid == DarkKnight.MONSTER_MAGNET) { // Monster Magnet diff --git a/src/net/server/worker/BaseWorker.java b/src/net/server/worker/BaseWorker.java new file mode 100644 index 0000000000..6404f50f76 --- /dev/null +++ b/src/net/server/worker/BaseWorker.java @@ -0,0 +1,39 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License 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.worker; + +import net.server.world.World; + +/** + * @author Ronan + */ +public class BaseWorker implements Runnable { + protected World wserv; + + @Override + public void run() {} + + public BaseWorker(World world) { + wserv = world; + } +} diff --git a/src/net/server/CharacterAutosaverWorker.java b/src/net/server/worker/CharacterAutosaverWorker.java similarity index 90% rename from src/net/server/CharacterAutosaverWorker.java rename to src/net/server/worker/CharacterAutosaverWorker.java index a121fdb87b..a871e5c70e 100644 --- a/src/net/server/CharacterAutosaverWorker.java +++ b/src/net/server/worker/CharacterAutosaverWorker.java @@ -20,17 +20,17 @@ along with this program. If not, see . */ -package net.server; +package net.server.worker; import net.server.world.World; import client.MapleCharacter; import constants.ServerConstants; +import net.server.PlayerStorage; /** * @author Ronan */ -public class CharacterAutosaverWorker implements Runnable { - private World wserv; +public class CharacterAutosaverWorker extends BaseWorker implements Runnable { @Override public void run() { @@ -45,6 +45,6 @@ public class CharacterAutosaverWorker implements Runnable { } public CharacterAutosaverWorker(World world) { - wserv = world; + super(world); } } diff --git a/src/net/server/CouponWorker.java b/src/net/server/worker/CouponWorker.java similarity index 96% rename from src/net/server/CouponWorker.java rename to src/net/server/worker/CouponWorker.java index cc1f1c7921..568517f708 100644 --- a/src/net/server/CouponWorker.java +++ b/src/net/server/worker/CouponWorker.java @@ -20,9 +20,10 @@ along with this program. If not, see . */ -package net.server; +package net.server.worker; import java.sql.SQLException; +import net.server.Server; import tools.FilePrinter; /** diff --git a/src/net/server/worker/HiredMerchantWorker.java b/src/net/server/worker/HiredMerchantWorker.java new file mode 100644 index 0000000000..4d7b3a7bfd --- /dev/null +++ b/src/net/server/worker/HiredMerchantWorker.java @@ -0,0 +1,40 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License 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.worker; + +import net.server.world.World; + +/** + * @author Ronan + */ +public class HiredMerchantWorker extends BaseWorker implements Runnable { + + @Override + public void run() { + wserv.runHiredMerchantSchedule(); + } + + public HiredMerchantWorker(World world) { + super(world); + } +} diff --git a/src/net/server/MountTirednessWorker.java b/src/net/server/worker/MountTirednessWorker.java similarity index 90% rename from src/net/server/MountTirednessWorker.java rename to src/net/server/worker/MountTirednessWorker.java index b204c4c6a1..b67889e0a3 100644 --- a/src/net/server/MountTirednessWorker.java +++ b/src/net/server/worker/MountTirednessWorker.java @@ -20,15 +20,14 @@ along with this program. If not, see . */ -package net.server; +package net.server.worker; import net.server.world.World; /** * @author Ronan */ -public class MountTirednessWorker implements Runnable { - private World wserv; +public class MountTirednessWorker extends BaseWorker implements Runnable { @Override public void run() { @@ -36,6 +35,6 @@ public class MountTirednessWorker implements Runnable { } public MountTirednessWorker(World world) { - wserv = world; + super(world); } } diff --git a/src/net/server/PetFullnessWorker.java b/src/net/server/worker/PetFullnessWorker.java similarity index 90% rename from src/net/server/PetFullnessWorker.java rename to src/net/server/worker/PetFullnessWorker.java index bb60f2325a..6830419a8b 100644 --- a/src/net/server/PetFullnessWorker.java +++ b/src/net/server/worker/PetFullnessWorker.java @@ -20,15 +20,14 @@ along with this program. If not, see . */ -package net.server; +package net.server.worker; import net.server.world.World; /** * @author Ronan */ -public class PetFullnessWorker implements Runnable { - private World wserv; +public class PetFullnessWorker extends BaseWorker implements Runnable { @Override public void run() { @@ -36,6 +35,6 @@ public class PetFullnessWorker implements Runnable { } public PetFullnessWorker(World world) { - wserv = world; + super(world); } } diff --git a/src/net/server/RankingWorker.java b/src/net/server/worker/RankingWorker.java similarity index 98% rename from src/net/server/RankingWorker.java rename to src/net/server/worker/RankingWorker.java index 586d4877bc..10eed76d7c 100644 --- a/src/net/server/RankingWorker.java +++ b/src/net/server/worker/RankingWorker.java @@ -19,7 +19,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.server; +package net.server.worker; import java.sql.Connection; import java.sql.PreparedStatement; @@ -28,6 +28,7 @@ import java.sql.SQLException; import client.MapleJob; import tools.DatabaseConnection; import constants.ServerConstants; +import net.server.Server; /** * @author Matze diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index e1f81313c5..565bd9e69b 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -45,9 +45,10 @@ import java.util.HashSet; import java.util.concurrent.ScheduledFuture; import server.TimerManager; -import net.server.CharacterAutosaverWorker; -import net.server.MountTirednessWorker; -import net.server.PetFullnessWorker; +import server.maps.HiredMerchant; +import net.server.worker.CharacterAutosaverWorker; +import net.server.worker.MountTirednessWorker; +import net.server.worker.PetFullnessWorker; import net.server.PlayerStorage; import net.server.Server; import net.server.channel.Channel; @@ -84,6 +85,10 @@ public class World { private ScheduledFuture mountsSchedule; private long mountUpdate; + private Map activeMerchants = new LinkedHashMap<>(); + private ScheduledFuture MerchantsSchedule; + private long merchantUpdate; + private ScheduledFuture charactersSchedule; public World(int world, int flag, String eventmsg, int exprate, int droprate, int mesorate, int bossdroprate) { @@ -773,6 +778,48 @@ public class World { } } } + + public void registerHiredMerchant(HiredMerchant hm) { + synchronized(activeMerchants) { + byte initProc; + if(System.currentTimeMillis() - merchantUpdate > 5 * 60 * 1000) initProc = 1; + else initProc = 0; + + activeMerchants.put(hm, initProc); + } + } + + public void unregisterHiredMerchant(HiredMerchant hm) { + synchronized(activeMerchants) { + activeMerchants.remove(hm); + } + } + + public void runHiredMerchantSchedule() { + Map deployedMerchants; + synchronized(activeMerchants) { + merchantUpdate = System.currentTimeMillis(); + deployedMerchants = Collections.unmodifiableMap(activeMerchants); + } + + for(Map.Entry dm: deployedMerchants.entrySet()) { + byte timeOn = dm.getValue(); + + if(timeOn <= 144) { // 1440 minutes == 24hrs + synchronized(activeMerchants) { + activeMerchants.put(dm.getKey(), (byte)(timeOn + 1)); + } + } else { + HiredMerchant hm = dm.getKey(); + hm.forceClose(); + this.getChannel(hm.getChannel()).removeHiredMerchant(hm.getOwnerId()); + + synchronized(activeMerchants) { + activeMerchants.remove(dm.getKey()); + } + } + } + } public void setServerMessage(String msg) { for (Channel ch : channels) { diff --git a/src/server/MapleInventoryManipulator.java b/src/server/MapleInventoryManipulator.java index b240e4d3a8..27fa978d16 100644 --- a/src/server/MapleInventoryManipulator.java +++ b/src/server/MapleInventoryManipulator.java @@ -523,7 +523,7 @@ public class MapleInventoryManipulator { return; } int itemId = source.getItemId(); - if (itemId >= 5000000 && itemId <= 5000102) { + if (ItemConstants.isPet(itemId)) { return; } if (type == MapleInventoryType.EQUIPPED && itemId == 1122017) { diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java index d2c8f3fc7d..d93f683f8a 100644 --- a/src/server/MapleItemInformationProvider.java +++ b/src/server/MapleItemInformationProvider.java @@ -110,6 +110,7 @@ public class MapleItemInformationProvider { protected Map consumeOnPickupCache = new HashMap<>(); protected Map isQuestItemCache = new HashMap<>(); protected Map equipmentSlotCache = new HashMap<>(); + protected Map noCancelMouseCache = new HashMap<>(); private MapleItemInformationProvider() { loadCardIdData(); @@ -246,7 +247,7 @@ public class MapleItemInformationProvider { } else if (itemId >= 1080000 && itemId < 1090000) { theData = eqpStringData; cat = "Eqp/Glove"; - } else if (itemId >= 30000 && itemId < 32000) { + } else if (itemId >= 30000 && itemId < 35000) { theData = eqpStringData; cat = "Eqp/Hair"; } else if (itemId >= 1050000 && itemId < 1060000) { @@ -278,7 +279,7 @@ public class MapleItemInformationProvider { cat = "Etc"; } else if (itemId >= 3000000 && itemId < 4000000) { theData = insStringData; - } else if (itemId >= 5000000 && itemId < 5010000) { + } else if (ItemConstants.isPet(itemId)) { theData = petStringData; } else { return null; @@ -291,11 +292,19 @@ public class MapleItemInformationProvider { } public boolean noCancelMouse(int itemId) { + if (noCancelMouseCache.containsKey(itemId)) { + return noCancelMouseCache.get(itemId); + } + MapleData item = getItemData(itemId); if (item == null) { + noCancelMouseCache.put(itemId, false); return false; } - return MapleDataTool.getIntConvert("info/noCancelMouse", item, 0) == 1; + + boolean blockMouse = MapleDataTool.getIntConvert("info/noCancelMouse", item, 0) == 1; + noCancelMouseCache.put(itemId, blockMouse); + return blockMouse; } private MapleData getItemData(int itemId) { diff --git a/src/server/MapleShop.java b/src/server/MapleShop.java index f52bc93220..df111ce591 100644 --- a/src/server/MapleShop.java +++ b/src/server/MapleShop.java @@ -132,7 +132,7 @@ public class MapleShop { int cardreduce = value - cost; int diff = cardreduce + c.getPlayer().getMeso(); if (MapleInventoryManipulator.checkSpace(c, itemId, quantity, "")) { - if (itemId >= 5000000 && itemId <= 5000100) { + if (ItemConstants.isPet(itemId)) { int petid = MaplePet.createPet(itemId); MapleInventoryManipulator.addById(c, itemId, quantity, null, petid, -1); } else { diff --git a/src/server/MapleStatEffect.java b/src/server/MapleStatEffect.java index 87be07ae6a..f184100895 100644 --- a/src/server/MapleStatEffect.java +++ b/src/server/MapleStatEffect.java @@ -565,8 +565,8 @@ public class MapleStatEffect { case Crusader.ARMOR_CRASH: case DragonKnight.POWER_CRASH: case WhiteKnight.MAGIC_CRASH: - monsterStatus.put(MonsterStatus.SEAL_SKILL, Integer.valueOf(1)); - break; + monsterStatus.put(MonsterStatus.SEAL_SKILL, Integer.valueOf(1)); + break; case Rogue.DISORDER: monsterStatus.put(MonsterStatus.WATK, Integer.valueOf(ret.x)); monsterStatus.put(MonsterStatus.WDEF, Integer.valueOf(ret.y)); @@ -842,7 +842,6 @@ public class MapleStatEffect { } else { MapleInventoryManipulator.removeById(applyto.getClient(), MapleInventoryType.USE, projectile, 200, false, true); } - } SummonMovementType summonMovementType = getSummonMovementType(); if (overTime || isCygnusFA() || summonMovementType != null) { @@ -983,9 +982,12 @@ public class MapleStatEffect { public void silentApplyBuff(MapleCharacter chr, long starttime) { int localDuration = duration; localDuration = alchemistModifyVal(chr, localDuration, false); - CancelEffectAction cancelAction = new CancelEffectAction(chr, this, starttime); - ScheduledFuture schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + localDuration) - System.currentTimeMillis())); - chr.registerEffect(this, starttime, schedule); + //CancelEffectAction cancelAction = new CancelEffectAction(chr, this, starttime); + //ScheduledFuture schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + localDuration) - System.currentTimeMillis())); + + if(starttime + localDuration <= System.currentTimeMillis()) return; + + chr.registerEffect(this, starttime, (starttime + localDuration), true); SummonMovementType summonMovementType = getSummonMovementType(); if (summonMovementType != null) { final MapleSummon tosummon = new MapleSummon(chr, sourceid, chr.getPosition(), summonMovementType); @@ -1006,7 +1008,18 @@ public class MapleStatEffect { final long starttime = System.currentTimeMillis(); // final CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime); // final ScheduledFuture schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + 99999) - System.currentTimeMillis())); - applyto.registerEffect(this, starttime, null); + applyto.registerEffect(this, starttime, Long.MAX_VALUE, false); + } + + public void updateBuffEffect(MapleCharacter target, List> activeStats, long starttime) { + int localDuration = duration; + localDuration = alchemistModifyVal(target, localDuration, false); + + long leftDuration = (starttime + localDuration) - System.currentTimeMillis(); + if(leftDuration > 0) { + byte[] buff = MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int)leftDuration, activeStats); + target.getClient().announce(buff); + } } private void applyBuffEffect(MapleCharacter applyfrom, MapleCharacter applyto, boolean primary) { @@ -1107,11 +1120,7 @@ public class MapleStatEffect { List> stat = Collections.singletonList(new Pair<>(MapleBuffStat.MORPH, Integer.valueOf(getMorph(applyto)))); mbuff = MaplePacketCreator.giveForeignBuff(applyto.getId(), stat); } - long starttime = System.currentTimeMillis(); - CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime); - ScheduledFuture schedule = TimerManager.getInstance().schedule(cancelAction, localDuration); - applyto.registerEffect(this, starttime, schedule); - + if (buff != null) { if (!hasNoIcon()) { //Thanks flav for such a simple release! :) applyto.getClient().announce(buff); @@ -1120,6 +1129,12 @@ public class MapleStatEffect { System.out.println(" NO buff icon for id " + sourceid); } } + + long starttime = System.currentTimeMillis(); + //CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime); + //ScheduledFuture schedule = TimerManager.getInstance().schedule(cancelAction, localDuration); + applyto.registerEffect(this, starttime, starttime + localDuration, false); + if (mbuff != null) { applyto.getMap().broadcastMessage(applyto, mbuff, false); } @@ -1511,11 +1526,16 @@ public class MapleStatEffect { public int getSourceId() { return sourceid; } + + public int getBuffSourceId() { + return skill ? sourceid : -sourceid; + } public boolean makeChanceResult() { return prop == 1.0 || Math.random() < prop; } + /* private static class CancelEffectAction implements Runnable { private MapleStatEffect effect; @@ -1536,6 +1556,7 @@ public class MapleStatEffect { } } } + */ public short getHp() { return hp; diff --git a/src/server/maps/HiredMerchant.java b/src/server/maps/HiredMerchant.java index 0f52e93c92..c5a1fc527e 100644 --- a/src/server/maps/HiredMerchant.java +++ b/src/server/maps/HiredMerchant.java @@ -35,12 +35,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.ScheduledFuture; import net.server.Server; import server.MapleInventoryManipulator; import server.MapleItemInformationProvider; import server.MaplePlayerShopItem; -import server.TimerManager; import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.Pair; @@ -61,7 +59,6 @@ public class HiredMerchant extends AbstractMapleMapObject { private List> messages = new LinkedList<>(); private List sold = new LinkedList<>(); private boolean open; - public ScheduledFuture schedule = null; private MapleMap map; public HiredMerchant(final MapleCharacter owner, int itemId, String desc) { @@ -74,14 +71,6 @@ public class HiredMerchant extends AbstractMapleMapObject { this.ownerName = owner.getName(); this.description = desc; this.map = owner.getMap(); - this.schedule = TimerManager.getInstance().schedule(new Runnable() { - - @Override - public void run() { - HiredMerchant.this.forceClose(); - Server.getInstance().getChannel(world, channel).removeHiredMerchant(ownerId); - } - }, 1000 * 60 * 60 * 24); } public void broadcastToVisitors(final byte[] packet) { @@ -194,9 +183,8 @@ public class HiredMerchant extends AbstractMapleMapObject { } public void forceClose() { - if (schedule != null) { - schedule.cancel(false); - } + Server.getInstance().getWorld(world).unregisterHiredMerchant(this); + try { saveItems(true); items.clear(); @@ -226,7 +214,6 @@ public class HiredMerchant extends AbstractMapleMapObject { } map = null; - schedule = null; } public void closeShop(MapleClient c, boolean timeout) { @@ -268,7 +255,8 @@ public class HiredMerchant extends AbstractMapleMapObject { } catch (Exception e) { e.printStackTrace(); } - schedule.cancel(false); + + Server.getInstance().getWorld(world).unregisterHiredMerchant(this); } public String getOwner() { diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 0d2c533a8a..0baa484756 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -83,7 +83,6 @@ import client.MapleCharacter.SkillEntry; import client.MapleClient; import client.MapleDisease; import client.MapleFamilyEntry; -import client.MapleJob; import client.MapleKeyBinding; import client.MapleMount; import client.MapleQuestStatus; diff --git a/wz/Item.wz/Consume/0202.img.xml b/wz/Item.wz/Consume/0202.img.xml index fbef1abeb8..7442d425d1 100644 --- a/wz/Item.wz/Consume/0202.img.xml +++ b/wz/Item.wz/Consume/0202.img.xml @@ -18580,4 +18580,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/String.wz/Consume.img.xml b/wz/String.wz/Consume.img.xml index 0e006365e0..f2e7a7367e 100644 --- a/wz/String.wz/Consume.img.xml +++ b/wz/String.wz/Consume.img.xml @@ -1,20 +1,20 @@ - - - + + + - - - + + + - - - + + + - - - + + + @@ -9188,4 +9188,20 @@ + + + + + + + + + + + + + + + +